摘要:禁止內聯腳本執行規則較嚴格,目前發現使用。合理使用上報可以及時發現,利于盡快修復問題。因為事件會從目標元素一層層冒泡至對象。允許給一個事件注冊多個監聽。表示在捕獲階段觸發,表示在冒泡階段觸發。
關于【Step-By-Step】
Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。每個周末我會仔細閱讀大家的答案,整理最一份較優答案出來,因本人水平有限,有誤的地方,大家及時指正。
如果想 加群 學習,關注公眾號,添加我為好友,我拉你進群。
更多優質文章可戳: https://github.com/YvetteLau/...
本周面試題一覽:
什么是XSS攻擊,XSS 攻擊可以分為哪幾類?我們如何防范XSS攻擊?
如何隱藏頁面中的某個元素?
瀏覽器事件代理機制的原理是什么?
setTimeout 倒計時為什么會出現誤差?
11. 什么是XSS攻擊,XSS攻擊可以分為哪幾類?我們如何防范XSS攻擊? 1. XSS攻擊XSS(Cross-Site Scripting,跨站腳本攻擊)是一種代碼注入攻擊。攻擊者在目標網站上注入惡意代碼,當被攻擊者登陸網站時就會執行這些惡意代碼,這些腳本可以讀取 cookie,session tokens,或者其它敏感的網站信息,對用戶進行釣魚欺詐,甚至發起蠕蟲攻擊等。
XSS 的本質是:惡意代碼未經過濾,與網站正常的代碼混在一起;瀏覽器無法分辨哪些腳本是可信的,導致惡意腳本被執行。由于直接在用戶的終端執行,惡意代碼能夠直接獲取用戶的信息,利用這些信息冒充用戶向網站發起攻擊者定義的請求。
XSS分類根據攻擊的來源,XSS攻擊可以分為存儲型(持久性)、反射型(非持久型)和DOM型三種。下面我們來詳細了解一下這三種XSS攻擊:
1.1 反射型XSS
當用戶點擊一個惡意鏈接,或者提交一個表單,或者進入一個惡意網站時,注入腳本進入被攻擊者的網站。Web服務器將注入腳本,比如一個錯誤信息,搜索結果等,未進行過濾直接返回到用戶的瀏覽器上。
反射型 XSS 的攻擊步驟:
攻擊者構造出特殊的 URL,其中包含惡意代碼。
用戶打開帶有惡意代碼的 URL 時,網站服務端將惡意代碼從 URL 中取出,拼接在 HTML 中返回給瀏覽器。
用戶瀏覽器接收到響應后解析執行,混在其中的惡意代碼也被執行。
惡意代碼竊取用戶數據并發送到攻擊者的網站,或者冒充用戶的行為,調用目標網站接口執行攻擊者指定的操作。
反射型 XSS 漏洞常見于通過 URL 傳遞參數的功能,如網站搜索、跳轉等。由于需要用戶主動打開惡意的 URL 才能生效,攻擊者往往會結合多種手段誘導用戶點擊。
POST 的內容也可以觸發反射型 XSS,只不過其觸發條件比較苛刻(需要構造表單提交頁面,并引導用戶點擊),所以非常少見。
如果不希望被前端拿到cookie,后端可以設置 httpOnly (不過這不是 XSS攻擊 的解決方案,只能降低受損范圍)
如何防范反射型XSS攻擊
對字符串進行編碼。
對url的查詢參數進行轉義后再輸出到頁面。
app.get("/welcome", function(req, res) { //對查詢參數進行編碼,避免反射型 XSS攻擊 res.send(`${encodeURIComponent(req.query.type)}`); });
1.2 DOM 型 XSS
DOM 型 XSS 攻擊,實際上就是前端 JavaScript 代碼不夠嚴謹,把不可信的內容插入到了頁面。在使用 .innerHTML、.outerHTML、.appendChild、document.write()等API時要特別小心,不要把不可信的數據作為 HTML 插到頁面上,盡量使用 .innerText、.textContent、.setAttribute() 等。
DOM 型 XSS 的攻擊步驟:
攻擊者構造出特殊數據,其中包含惡意代碼。
用戶瀏覽器執行了惡意代碼。
惡意代碼竊取用戶數據并發送到攻擊者的網站,或者冒充用戶的行為,調用目標網站接口執行攻擊者指定的操作。
如何防范 DOM 型 XSS 攻擊
防范 DOM 型 XSS 攻擊的核心就是對輸入內容進行轉義(DOM 中的內聯事件監聽器和鏈接跳轉都能把字符串作為代碼運行,需要對其內容進行檢查)。
1.對于url鏈接(例如圖片的src屬性),那么直接使用 encodeURIComponent 來轉義。
2.非url,我們可以這樣進行編碼:
function encodeHtml(str) { return str.replace(/"/g, """) .replace(/"/g, "'") .replace(//g, ">"); }
DOM 型 XSS 攻擊中,取出和執行惡意代碼由瀏覽器端完成,屬于前端 JavaScript 自身的安全漏洞。
1.3 存儲型XSS
惡意腳本永久存儲在目標服務器上。當瀏覽器請求數據時,腳本從服務器傳回并執行,影響范圍比反射型和DOM型XSS更大。存儲型XSS攻擊的原因仍然是沒有做好數據過濾:前端提交數據至服務端時,沒有做好過濾;服務端在接受到數據時,在存儲之前,沒有做過濾;前端從服務端請求到數據,沒有過濾輸出。
存儲型 XSS 的攻擊步驟:
攻擊者將惡意代碼提交到目標網站的數據庫中。
用戶打開目標網站時,網站服務端將惡意代碼從數據庫取出,拼接在 HTML 中返回給瀏覽器。
用戶瀏覽器接收到響應后解析執行,混在其中的惡意代碼也被執行。
惡意代碼竊取用戶數據并發送到攻擊者的網站,或者冒充用戶的行為,調用目標網站接口執行攻擊者指定的操作。
這種攻擊常見于帶有用戶保存數據的網站功能,如論壇發帖、商品評論、用戶私信等。
如何防范存儲型XSS攻擊:
前端數據傳遞給服務器之前,先轉義/過濾(防范不了抓包修改數據的情況)
服務器接收到數據,在存儲到數據庫之前,進行轉義/過濾
前端接收到服務器傳遞過來的數據,在展示到頁面前,先進行轉義/過濾
除了謹慎的轉義,我們還需要其他一些手段來防范XSS攻擊:
1.Content Security Policy
在服務端使用 HTTP的 Content-Security-Policy 頭部來指定策略,或者在前端設置 meta 標簽。
例如下面的配置只允許加載同域下的資源:
Content-Security-Policy: default-src "self"
前端和服務端設置 CSP 的效果相同,但是meta無法使用report
嚴格的 CSP 在 XSS 的防范中可以起到以下的作用:
禁止加載外域代碼,防止復雜的攻擊邏輯。
禁止外域提交,網站被攻擊后,用戶的數據不會泄露到外域。
禁止內聯腳本執行(規則較嚴格,目前發現 GitHub 使用)。
禁止未授權的腳本執行(新特性,Google Map 移動版在使用)。
合理使用上報可以及時發現 XSS,利于盡快修復問題。
2.輸入內容長度控制
對于不受信任的輸入,都應該限定一個合理的長度。雖然無法完全防止 XSS 發生,但可以增加 XSS 攻擊的難度。
3.輸入內容限制
對于部分輸入,可以限定不能包含特殊字符或者僅能輸入數字等。
4.其他安全措施
HTTP-only Cookie: 禁止 JavaScript 讀取某些敏感 Cookie,攻擊者完成 XSS 注入后也無法竊取此 Cookie。
驗證碼:防止腳本冒充用戶提交危險操作。
點擊查看更多12.如何隱藏頁面中的某個元素? 隱藏類型
屏幕并不是唯一的輸出機制,比如說屏幕上看不見的元素(隱藏的元素),其中一些依然能夠被讀屏軟件閱讀出來(因為讀屏軟件依賴于可訪問性樹來闡述)。為了消除它們之間的歧義,我們將其歸為三大類:
完全隱藏:元素從渲染樹中消失,不占據空間。
視覺上的隱藏:屏幕中不可見,占據空間。
語義上的隱藏:讀屏軟件不可讀,但正常占據空。
完全隱藏display: none;
HTML5 新增屬性,相當于 display: none
視覺上的隱藏
設置 posoition 為 absolute 或 fixed,通過設置 top、left 等值,將其移出可視區域。(可視區域不占位)
position:absolute; left: -99999px;
設置 position 為 relative,通過設置 top、left 等值,將其移出可視區域。(可視區域占位)
position: relative; left: -99999px;
如希望其在可視區域不占位置,需同時設置 height: 0;
設置 margin 值,將其移出可視區域范圍(可視區域占位)。
margin-left: -99999px;
如果希望其在可視區域不占位,需同時設置 height: 0;
縮放(占據空間)
transform: scale(0);
如果希望不占據空間,需同時設置 height: 0
移動 translateX, translateY(占據空間)
transform: translateX(-99999px);
如果希望不占據空間,需同時設置 height: 0
旋轉 rotate (占據空間)
transform: rotateY(90deg);
寬高為0,字體大小為0:
height: 0; width: 0; font-size: 0;
寬高為0,超出隱藏:
height: 0; width: 0; overflow: hidden;
opacity: 0;
visibility: hidden
position: relative; z-index: -999;
再設置一個層級較高的元素覆蓋在此元素上。
clip-path: polygon(0 0, 0 0, 0 0, 0 0);語義上的隱藏
讀屏軟件不可讀,占據空間,可見。
11. 使用JS將元素從頁面中移除
點擊查看更多13.瀏覽器事件代理機制的原理是什么? 事件流
在說瀏覽器事件代理機制原理之前,我們首先了解一下事件流的概念,早期瀏覽器,IE采用的是事件捕獲事件流,而Netscape采用的則是事件冒泡。"DOM2級事件"把事件流分為三個階段,捕獲階段、目標階段、冒泡階段。現代瀏覽器也都遵循此規范。
事件代理機制的原理事件代理又稱為事件委托,在祖先級 DOM 元素綁定一個事件,當觸發子孫級DOM元素的事件時,利用事件冒泡的原理來觸發綁定在祖先級 DOM 的事件。因為事件會從目標元素一層層冒泡至 document 對象。
為什么要事件代理?添加到頁面上的事件數量會影響頁面的運行性能,如果添加的事件過多,會導致網頁的性能下降。采用事件代理的方式,可以大大減少注冊事件的個數。
事件代理的當時,某個子孫元素是動態增加的,不需要再次對其進行事件綁定。
不用擔心某個注冊了事件的DOM元素被移除后,可能無法回收其事件處理程序,我們只要把事件處理程序委托給更高層級的元素,就可以避免此問題。
允許給一個事件注冊多個監聽。
提供了一種更精細的手段控制 listener 的觸發階段(可以選擇捕獲或者是冒泡)。
對任何 DOM 元素都是有效的,而不僅僅是對 HTML 元素有效。
addEventListeneraddEventListener 接受3個參數,分別是要處理的事件名、實現了 EventListener 接口的對象或者是一個函數、一個對象/一個布爾值。
target.addEventListener(type, listener[, options]); target.addEventListener(type, listener[, useCapture]);
options(對象) | 可選
capture: Boolean。true 表示在捕獲階段觸發,false表示在冒泡階段觸發。默認是 false。
once:Boolean。true 表示listener 在添加之后最多只調用一次,listener 會在其被調用之后自動移除。默認是 false。
passive: Boolean。true 表示 listener 永遠不會調用 preventDefault()。如果 listener 仍然調用了這個函數,客戶端將會忽略它并拋出一個控制臺警告。默認是 false。
useCapture(Boolean) | 可選
useCapture 默認為 false。表示冒泡階段調用事件處理程序,若設置為 true,表示在捕獲階段調用事件處理程序。
如將頁面中的所有click事件都代理到document上:
document.addEventListener("click", function (e) { console.log(e.target); /** * 捕獲階段調用調用事件處理程序,eventPhase是 1; * 處于目標,eventPhase是2 * 冒泡階段調用事件處理程序,eventPhase是 3; */ console.log(e.eventPhase); }, false);
與 addEventListener 相對應的是 removeEventListener,用于移除事件監聽。
點擊查看更多14. setTimeout 倒計時為什么會出現誤差?
setTimeout 只能保證延時或間隔不小于設定的時間。因為它實際上只是將回調添加到了宏任務隊列中,但是如果主線程上有任務還沒有執行完成,它必須要等待。
如果你對前面這句話不是非常理解,那么有必要了解一下 JS的運行機制。
JS的運行機制(1)所有同步任務都在主線程上執行,形成一個執行棧(execution context stack)。
(2)主線程之外,還存在"任務隊列"(task queue)。
(3)一旦"執行棧"中的所有同步任務執行完畢,系統就會讀取"任務隊列",看看里面有哪些事件。那些對應的異步任務,于是結束等待狀態,進入執行棧,開始執行。
(4)主線程不斷重復上面的第三步。
如 setTimeout(()=>{callback();}, 1000) ,即表示在1s之后將 callback 放到宏任務隊列中,當1s的時間到達時,如果主線程上有其它任務在執行,那么 callback 就必須要等待,另外 callback 的執行也需要時間,因此 setTimeout 的時間間隔是有誤差的,它只能保證延時不小于設置的時間。
如何減少 setTimeout 的誤差我們只能減少執行多次的 setTimeout 的誤差,例如倒計時功能。
倒計時的時間通常都是從服務端獲取的。造成誤差的原因:
1.沒有考慮誤差時間(函數執行的時間/其它代碼的阻塞)
2.沒有考慮瀏覽器的“休眠”
完全消除 setTimeout的誤差是不可能的,但是我們減少 setTimeout 的誤差。通過對下一次任務的調用時間進行修正,來減少誤差。
let count = 0; let countdown = 5000; //服務器返回的倒計時時間 let interval = 1000; let startTime = new Date().getTime(); let timer = setTimeout(countDownStart, interval); //首次執行 //定時器測試 function countDownStart() { count++; const offset = new Date().getTime() - (startTime + count * 1000); const nextInterval = interval - offset; //修正后的延時時間 if (nextInterval < 0) { nextInterval = 0; } countdown -= interval; console.log("誤差:" + offset + "ms,下一次執行:" + nextInterval + "ms后,離活動開始還有:" + countdown + "ms"); if (countdown <= 0) { clearTimeout(timer); } else { timer = setTimeout(countDownStart, nextInterval); } }
如果當前頁面是不可見的,那么倒計時會出現大于100ms的誤差時間。因此在頁面顯示時,應該重新從服務端獲取剩余時間進行倒計時。當然,為了更好的性能,當倒計時不可見(Tab頁切換/倒計時內容不在可視區時),可以選擇停止倒計時。
為此,我們可以監聽 visibityChange 事件進行處理。
點擊查看更多參考文章:
[1] MDN addEventListener
[2] https://www.ecma-internationa...
[3] http://www.xuanfengge.com/js-...
謝謝各位小伙伴愿意花費寶貴的時間閱讀本文,如果本文給了您一點幫助或者是啟發,請不要吝嗇你的贊和Star,您的肯定是我前進的最大動力。https://github.com/YvetteLau/...
關注公眾號,加入技術交流群。
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/104555.html
摘要:關于點擊進入項目是我于開始的一個項目,每個工作日發布一道面試題。那個率先改變的實例的返回值,就傳遞給的回調函數。通過插入標簽的方式來實現跨域,參數只能通過傳入,僅能支持請求。因此清除浮動,只需要觸發一個即可。 關于【Step-By-Step】 Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。每個周末我會仔細閱讀大家的...
摘要:關于點擊進入項目是我于開始的一個項目,每個工作日發布一道面試題。即使這個時間周期內,小明取得多次滿分。創建作用域鏈在執行期上下文的創建階段,作用域鏈是在變量對象之后創建的。這種一層一層的關系,就是作用域鏈。 關于【Step-By-Step】 Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。每個周末我會仔細閱讀大家的答...
摘要:關于點擊進入項目是我于開始的一個項目,每個工作日發布一道面試題。即使這個時間周期內,小明取得多次滿分。創建作用域鏈在執行期上下文的創建階段,作用域鏈是在變量對象之后創建的。這種一層一層的關系,就是作用域鏈。 關于【Step-By-Step】 Step-By-Step (點擊進入項目) 是我于 2019-05-20 開始的一個項目,每個工作日發布一道面試題。每個周末我會仔細閱讀大家的答...
閱讀 4698·2021-09-22 16:06
閱讀 2082·2021-09-22 15:22
閱讀 1430·2019-08-30 15:54
閱讀 2519·2019-08-30 15:44
閱讀 2346·2019-08-29 16:31
閱讀 2016·2019-08-29 16:26
閱讀 2334·2019-08-29 12:41
閱讀 738·2019-08-29 12:22