XSS(Cross-Site Scripting,跨域腳本攻擊)攻擊是最常見的 Web 攻擊,是一種代碼注入攻擊。攻擊者通過在目標網站上注入惡意腳本,使之在用戶的瀏覽器上運行。利用這些惡意腳本,攻擊者可獲取用戶的敏感信息如 Cookie、SessionID 等,進而危害數據安全。其重點是『跨域』和『客戶端執行』。
XSS 的本質:
XSS 攻擊一般存在以下幾類:
反射型的 XSS 攻擊,主要是由於服務端接收到客戶端的不安全輸入,在客戶端觸發執行從而發起 Web 攻擊。
具體而言,反射型 XSS 只是簡單地把用戶輸入的數據 「反射」 給瀏覽器,這種攻擊方式往往需要攻擊者誘使用戶點擊一個惡意鏈接,或者提交一個表單,或者進入一個惡意網站時,注入腳本進入被攻擊者的網站。這是一種非持久型的攻擊。
比如:在某購物網站搜索物品,搜索結果會顯示搜索的關鍵詞。搜索關鍵詞填入<script>alert(handsome boy)</script>,點擊搜索。頁面沒有對關鍵詞進行過濾,這段代碼就會直接在頁面上執行,彈出 alert。
<script>alert(handsome boy)</script>
基於存儲的 XSS 攻擊,是通過提交帶有惡意腳本的內容存儲在伺服器上,當其他人看到這些內容時發起 Web 攻擊。一般提交的內容都是通過一些富文本編輯器編輯的,很容易插入危險代碼。
比較常見的一個場景是攻擊者在社區或論壇上寫下一篇包含惡意 JavaScript 代碼的文章或評論,文章或評論發表後,所有訪問該文章或評論的用戶,都會在他們的瀏覽器中執行這段惡意的 JavaScript 代碼。這是一種持久型的攻擊。
基於 DOM 的 XSS 攻擊是指通過惡意腳本修改頁面的 DOM 結構,是純粹發生在客戶端的攻擊。
DOM 型 XSS 跟前兩種 XSS 的區別:DOM 型 XSS 攻擊中,取出和執行惡意代碼由瀏覽器端完成,屬於前端 JavaScript 自身的安全漏洞,而其他兩種 XSS 都屬於服務端的安全漏洞。舉個栗子??:
<input type="text" id="input"> <button id="btn">Submit</button> <div id="div"></div> <script> const input = document.getElementById(input); const btn = document.getElementById(btn); const div = document.getElementById(div);
let val;
input.addEventListener(change, (e) => { val = e.target.value; }, false);
btn.addEventListener(click, () => { div.innerHTML = `<a href=${val}>testLink</a>` }, false); </script>
點擊 Submit 按鈕後,會在當前頁面插入一個鏈接,其地址為用戶的輸入內容。如果用戶在輸入時構造了如下內容:
" onclick=alert(/xss/)
用戶提交之後,頁面代碼就變成了:
<a href onlick="alert(/xss/)">testLink</a>
此時,用戶點擊生成的鏈接,就會執行對應的腳本。DOM 型 XSS 攻擊,實際上就是網站前端 JavaScript 代碼本身不夠嚴謹,把不可信的數據當作代碼執行了。在使用 .innerHTML、.outerHTML、document.write() 時要特別小心,不要把不可信的數據作為 HTML 插到頁面上,而應盡量使用 .textContent、.setAttribute() 等。
.innerHTML
.outerHTML
document.write()
.textContent
.setAttribute()
location
onclick
onerror
onload
onmouseover
<a>
href
eval()
setTimeout()
setInterval()
JSONP 的 callback 參數非常危險,他有兩種風險可能導致 XSS:
瀏覽器為了保證跨域訪問的安全性,會默認發一個 callback 參數到後台,介面拿到這個參數之後,需要將返回的 JSON 數據外麵包上 callback 參數。
具體的返回格式:
CALLBACK(JSON)
如果 ajax 請求是 JSONP 請求,返回的內容瀏覽器還會自動檢測,如果不是按這個格式返回或者 callback 的內容不對,這次請求就算失敗了。
這裡有一個機制,那就是請求的 callback 會被放入返回的內容當中,這也是可能出問題的地方。舉個栗子,如果返回的頁面,那麼 Content-Type: text/html,那麼 callback 注入的 html 元素都可以直接放到頁面上了。那麼,html 頁面必然不能支持 callback。支持 JSONP 的鏈接如果直接放到瀏覽器裡面訪問,瀏覽器就不會做 callback 校驗了。
Content-Type: text/html
2.1 防禦 XSS 的根本之道
通過前面的介紹可以得知,XSS 攻擊有兩大要素:
根本的解決方法:從輸入到輸出都需要過濾、轉義。
輸入指客戶端請求參數,具體包括:
針對 HTML 代碼的編碼方式是 HTMLEncode,它的作用是將字元串轉換成 HTMLEntities。目前來說,為了對抗 XSS,需要對以下六個字元進行實體化轉義。
當然,上面的只是最基本而且是最必要的,HTMLEncode 還有很多很多,具體可以參考:Web安全系列(四):XSS 的防禦 | 掘金 一文中提及的特殊字元。
除此之外,富文本的輸入需要額外注意:
htmlParser
<iframe>
<script>
<base>
<form>
<img>
div
不要以為在輸入的時候進行過濾就萬事大吉了,惡意攻擊者們可能會層層繞過防禦機制進行 XSS 攻擊,一般來說,所有需要輸出到 HTML 頁面的變數,全部需要使用編碼或者轉義來防禦。輸出需要轉義的部分,具體包括:
HTML 的部分和輸入的轉義方式相同,使用 HTMLEncode,此處不再複述。
JavaScript 的部分同樣需要編碼轉義,比如在 JSONP 中可以通過意外截斷 JSON 數據或者在頁面中玩轉引號來造成 XSS 攻擊。
let a = "我是變數" // 我是變數 = ";alert(1);// a = "";alert(1);//"
攻擊者只需要閉合標籤就能實行攻擊,目前的防禦方法就是 JavaScriptEncode。JavaScriptEncode 與 HTMLEncode 的編碼方式不同,它需要用 對特殊字元進行轉義。
在 CSS 中或者 style 標籤中的攻擊花樣特別多,具體可以參考:Web安全系列(四):XSS 的防禦 | 掘金。此處由於篇幅問題,僅僅談及一下解決方案。
要解決 CSS 的攻擊問題,一方面要嚴格控制用戶將變數輸入 style 標籤內,另一方面不要引用未知的 CSS 文件,如果一定有用戶改變 CSS 變數這種需求的話,可以使用 OWASP ESAPI 中的 encodeForCSS() 函數。
OWASP ESAPI
encodeForCSS()
在 URL 中的輸出直接使用 URLEncode 即可,需要轉義變數的部分。
Content-Type: application / json
[
]
a-zA-Z0123456789_
$
.
/**/
while(1) ;
這是瀏覽器自帶的防範能力,一般是通過開啟 Web 安全頭生效的。具體有以下幾個:
text/plain
text/html
HttpOnly 最早由微軟提出,至今已經成為一個標準。瀏覽器將禁止頁面的 Javascript 訪問帶有 HttpOnly 屬性的 Cookie。
// 利用 express 設置 cookie 並開啟 httpOnly res.cookie(myCookie, test, { httpOnly: true })
防止腳本冒充用戶提交危險操作。
整體的 XSS 防範是非常複雜和繁瑣的,我們不僅需要在全部需要轉義的位置,對數據進行對應的轉義。而且要防止多餘和錯誤的轉義,避免正常的用戶輸入出現亂碼。雖然很難通過技術手段完全避免 XSS,但我們可以總結以下原則減少漏洞的產生:
以下經驗總結摘自《【基本功】 前端安全系列之一:如何防止XSS攻擊? | 美團技術團隊》
<%= data %>
<%- data %>
{{! data }
{{= data }
onLoad="onload({{data}})"
onClick="go({{action}})"
.addEventlistener()
通過 CSP、輸入長度配置、介面安全措施等方法,增加攻擊的難度,降低攻擊的後果。
推薦閱讀:
TAG:Web開發 | 網路安全 |