目錄
- 什么是xss攻擊?
- XSS的危害
- XSS攻擊分類
- xss攻擊示例
- 反射型攻擊 - 前端URL參數解析
- 反射型攻擊 - 后端URL參數解析
- 注入型攻擊 - 留言評論
- 如何規避xss攻擊?
- 總結
什么是xss攻擊?
XSS,即(Cross Site Scripting)中文名稱為“跨站腳本攻擊”。
XSS的重點不在於跨站攻擊而在於腳本攻擊。攻擊者可以利用 web應用的漏洞或缺陷之處,向頁面注入惡意的程序或代碼,以達到攻擊的目的。
通俗的來說就是我們的頁面在加載並且渲染繪制的過程中,如果加載並執行了意料之外的程序或代碼(腳本、樣式),就可以認為是受到了 XSS攻擊。
XSS的危害
- 通過
document.cookie盜取 cookie中的信息 - 使用 js或 css破壞頁面正常的結構與樣式
- 流量劫持(通過訪問某段具有
window.location.href定位到其他頁面) - dos攻擊:利用合理的客戶端請求來占用過多的服務器資源,從而使合法用戶無法得到服務器響應。並且通過攜帶過程的 cookie信息可以使服務端返回400開頭的狀態碼,從而拒絕合理的請求服務。
- 利用 iframe、frame、XMLHttpRequest或上述 Flash等方式,以(被攻擊)用戶的身份執行一些管理動作,或執行一些一般的如發微博、加好友、發私信等操作,並且攻擊者還可以利用 iframe,frame進一步的進行 CSRF 攻擊。
- 控制企業數據,包括讀取、篡改、添加、刪除企業敏感數據的能力。
XSS攻擊分類
XSS 根據攻擊是否持久,可以分為 “反射型XSS”與“存儲型XSS”兩種。
“反射型XSS”攻擊者通過包裝特定的連接,並將鏈接發送給實際的用戶來進行攻擊。
“反射型XSS”攻擊一般是利用前端代碼的漏洞或缺陷,比如使用 Eval來解析執行動態傳入的數據,或者是一些被后端接受處理后再返回前端展示的 URL參數等,其操作手法很類似於釣魚攻擊。
“存儲型XSS”攻擊是通過表單提交,抓包工具,直接調用接口等形式向后端數據庫注入數據。一旦被注入成功,並在輸出的頁面上沒有做任何防范措施,那么所有訪問這個頁面的用戶都會被攻擊。
總的來說,“反射型XSS”是一種局部非持久的針對性攻擊,而“存儲型XSS”就要嚴重的多,它是一個全面大范圍可持久型的攻擊。
xss攻擊示例
反射型攻擊 - 前端URL參數解析
正常頁面鏈接:
http://xss-example.com/index.html?data={}
攻擊者包裝后的鏈接(data可能是需要Eval解析的Json數據)
http://xss-example.com/index.html?data=alert(documet.cookie)
前端會被攻擊的代碼
var data = eval('('+ getUrlParams('data') +')');
PS:當然在實際情況下,攻擊者是不會把攻擊代碼這么明顯的暴漏出來,一般都會經過編碼。
http://xss-example.com/index.html?data=\u0061\u006c\u0065\u0072\u0074(1)
如果你認為在解析 URL參數時不使用 Eval 便能保證安全,那就大錯特錯了,因為攻擊者往往會主動的幫你執行 eval。
http://xss-example.com/index.html?url=http://exmaple.com
然后前端代碼去解析並埋入一個 <a> 標記中。
var _href = getUrlParams('url');
$('a').attr('href', _href);
但是,現在如果攻擊者利用了您這個功能包裝了這樣的一條鏈接呢?
http://xss-example.com/index.html?url=javascript:eval(alert(document.cookie));
編碼之后:
http://xss-example.com/index.html?url=javascript:\u0065\u0076\u0061\u006c(\u0061\u006c\u0065\u0072\u0074(document.cookie));
反射型攻擊 - 后端URL參數解析
現在有這樣一個鏈接,URL參數會被后端的程序解析並返回給前端頁面。
http://xss-example.com/index.html?name="xiaoming";
后端代碼示例:
app.get('index.html', function (req, res) {
res.send(req.query.name);
})
如果現在用戶訪問的是這樣的連接,有會怎么樣呢?
http://xss-example.com/index.html?name=<script>alert(document.cookie)</script>
此時一個腳本標記就會被后端代碼重新下發給前端,然后前端將其加入頁面中,便會觸發攻擊行為。
當然,實際中並沒有這么可怕,因為 web程序本身就已經很好的進行了阻攔過濾,但是經過我的實際測試發現 Firefox與老版本的IE依然有這些問題,只有 Chrome 與新版本的IE進行了阻止。
如果你想再Chrome與新版本的IE瀏覽器中看到實際可產生的效果,可以通過設置 HTTP 的 Header頭來關閉瀏覽器自動阻攔與過濾XSS功能。
app.get('index.html', function (req, res) {
res.set('X-XSS-Protection', 0); //此處是關鍵
res.send(req.query.name);
});
注入型攻擊 - 留言評論
注入型攻擊常見的地方就是留言評論或者是含有表單提交的地方。
例如下面我們就以要給留言評論為例子來說明注入型攻擊:
首先,攻擊者向一個textarea輸入以下內容:
<script>alert(document.cookie)</script>
然后,前端調用 ajax 向后端傳值
$('.send').click(function(){
$.post('message.htm',{'msg':$('textarea').val()},function(){});
});
接着,后端接收值寫入數據庫,同時又返回給前端展示。
app.post('message.htm',function(req,res,next){
//寫入數據庫
//...
//響應前端
res.json({
test: req.body.msg
})
});
最終當前端原樣展示之前輸入的攻擊代碼時,頁面便發生了存儲型攻擊。
不論是反射型攻擊還是存儲型,攻擊者總需要找到兩個要點,即“輸入點”與"輸出點",也只有這兩者都滿足,XSS攻擊才會生效。“輸入點”用於向 web頁面注入所需的攻擊代碼,而“輸出點”就是攻擊代碼被執行的地方。
大致上,攻擊者進行XSS攻擊要經過以下幾個步驟:
![[圖]](/image/aHR0cDovL3N0YXRpYy56eWJ1bHVvLmNvbS9zaGVuZ3VvdGFvL3Y1d21ob2Y0dHQyeGw1ZXozZzh3OHlzNi94c3Mtc3RlcC5wbmc=.png)
首先是分析程序尋找漏洞,然后構建攻擊代碼,比如上面作為留言內容的 script 標簽,實際上可以執行前端JS代碼,遠程加載JS腳本CSS樣式文件的 HTML標簽也非常多,比如:
#當圖片不存在時,必然觸發 onerror事件
<img src="null" onerror='alert(document.cookie)' />
#加載遠程CSS文件,破壞當前頁面的樣式
<link href="test.css">
#點擊的時候
<a onclick="alert(document.cookie)" onmouseover onmouseout></a>
#鼠標移動的時候
<div onmouseover=‘do something here’>
#破壞頁面樣式
<style>*{font-size:100px}</style>
#利用IE7-的 css expression表達式的行為。
<div style="width:expression(alert('XSS'))">
當代碼注入成功后,攻擊者往往就需要去尋找所注入代碼的輸出點,例如百度網盤之前就有一個修改昵稱的 XSS漏洞,雖然前端設置了字符長度為10個字符,但是攻擊者通過使用抓包工具構建了一個 的執行腳本,並成功的注入到了數據庫,后面測試發現,最終攻擊的輸出位置處於用戶分享資源給其它好友時,展開好友列表的時刻。
如何規避xss攻擊?
實際上簡單的通過正則判斷 script、link、style、img 等HTML標記並不可取,因為,首先輸入點的情況變化多樣,很難把所有的 html標記的特性都考慮進來,其次對html標記的限制,也會讓產品的可用性大大降低(比如有些特殊的關鍵字會被程序阻止,使得用戶使用非常不便),最后這種判斷本身也不安全,比如攻擊者會在關鍵字中插入空格、制表符以及其它HTML實體編碼來躲避偵測。
既然我們前面說到攻擊必須有兩個要點:“輸入點”,“輸出點”,所以防御的時候,我們只要做好這兩個點的控制,就基本上可以萬無一失!
- 對輸入內容的特定字符進行編碼,例如表示 html標記的 < > 等符號。
- 對重要的 cookie設置 httpOnly, 防止客戶端通過
document.cookie讀取 cookie,此 HTTP頭由服務端設置。 - 將不可信的值輸出 URL參數之前,進行 URLEncode操作,而對於從 URL參數中獲取值一定要進行格式檢測(比如你需要的時URL,就判讀是否滿足URL格式)。
- 不要使用 Eval來解析並運行不確定的數據或代碼,對於 JSON解析請使用
JSON.parse()方法。 - 后端接口也應該要做到關鍵字符過濾的問題。
就目前而言,應對XSS攻擊的主要手段還是編碼與過濾兩種,編碼用於將特殊的符號 "<、>、&、'、""進行轉義,而過濾則是阻止特定的標記、屬性、事件。
如果你不願意為了嚴格的安全而限制產品本身的靈活,那么我更建議采用“編碼”的方案。
首先看下京東的搜索功能:

接着,再看下知乎提交評論時接口的數據:

最后,我們再看下知乎時如何展示提交后的評論:

實際上實現上述的編碼功能非常簡單,我們可以對照 HTML實體編碼表 來進行正則匹配替換。
function encode(str) {
if (!str || str.length === 0) return '';
str = str.replace(/>/gm, '>');
str = str.replace(/</gm, '<');
str = str.replace(/"/gm, '"');
str = str.replace(/'/gm, ''');
return str;
}
當然在實際應用中很難避免自己寫的匹配規則就能萬無一失,並且XSS攻擊又一直是在變化的過程中,因此個人更推薦使用第三方專門防御XSS攻擊的庫。
前面都是針對輸入點的防御說明,在輸出的情況下,前端開發人員應當要對自己采用的輸出方法與輸出方式要有一定的了解:
原生JS中
| 方法 | 說明 |
|---|---|
| innerHTML | 安全,但是IE8下危險(IE8支持可見的含有defers屬性的script標記,例如:_) |
| appendChild, insertBefored等 | 危險 |
| innerText | 安全 |
Jquery中
| 方法 | 說明 |
|---|---|
| html() | 危險 |
| before,after,append等 | 危險 |
| text() | 安全 |
EJS模版
| 輸出格式 | 說明 |
|---|---|
| <%= > | 危險,非轉義的HTML輸出 |
| <%- > | 安全,轉義的HTML輸出 |
總結
首先我們了解了 XSS的定義,XSS即跨站點腳本攻擊,只要瀏覽器加載,解析,執行了意料之外的JS,CSS等都可以被認為是受到了 XSS攻擊,而 XSS攻擊的分類主要有“反射型”與“存儲型”兩種。
“反射型”攻擊者通過包裝改造URL參數,然后利用前端代碼的缺陷或漏洞來攻擊,它更偏向與前端層面,並且在實際攻擊中攻擊者會根據 HTML實體編碼、URL編碼、uniocde編碼等進行編碼然后欺騙用戶點擊訪問。而“存儲型”攻擊者則會通過抓包工具或者是直接調用接口的方式想盡一切辦法來向后端數據庫注入數據。
XSS攻擊有兩個要點,一個是“輸入點”,針對輸入點我們可以對關鍵的特殊的字符進行編碼,而在“輸出點”我們要對自己采用的輸出方式以及方法要有一定的安全風險認知。
https://www.owasp.org/index.php/DOM_based_XSS_Prevention_Cheat_Sheet (查看更多XSS攻擊案例)
