XSS的定義
攻擊者利用網站漏洞把惡意的指令碼程式碼(通常包括HTML程式碼和JavaScript指令碼)注入到網頁中,當其他使用者瀏覽這些網頁時,就會執行其中的惡意程式碼,對受害使用者可能採取cookie竊取、會話劫持、釣魚欺騙。
XSS的攻擊方式
反射型
發出請求時,XSS程式碼出現在URL中,作為輸入提交到伺服器端,伺服器解析後響應,XSS程式碼隨響應內容一起傳回瀏覽器,最後瀏覽器解析執行XSS程式碼。這個過程像一次反射,故叫反射XSS。
舉例
本例使用nodejs構建專案,演示反射型XSS攻擊。
建立目錄
首先,我們先建立一個目錄xssfilter
並且進入該目錄
$ mkdir xssfilter
$ cd xssfilter
複製程式碼
在該目錄繼續建立一個目錄xss
,用於建立我們整個的XSS攻擊的模擬服務。
$ mkdir xss
$ cd xss
複製程式碼
搭建web專案
使用express框架快速搭建整個的應用服務
$ express -e ./
複製程式碼
依賴
安裝一下所有的依賴
$ npm install
複製程式碼
目錄結構
看一下目錄結構
啟動服務
$ npm start
複製程式碼
頁面
訪問localhost:3000
,看是否展示成功
演示
接下來我們就要開始反射型XSS的演示了
js
xss:req.query.xss
複製程式碼
我們在此設定一個查詢的欄位來獲取使用者在URL中寫的search內容
檢視層
<div class="">
<%- xss%>
</div>
複製程式碼
我們在檢視層展示這個內容
接下來重啟一下,檢視一下帶有值後展示的是啥樣的
由此,我們發現,值已經展示。
接下來,我們開始一些有攻擊性的指令碼
xss=<img src="null" onerror="alert(1);"/>
複製程式碼
開啟頁面發現
這是因為Chrome會自動攔截XSS攻擊
我們設定下不讓瀏覽器對XSS攔截
res.set('X-XSS-Protection',0);
複製程式碼
重啟服務,開啟瀏覽我們會看到XSS攻擊成功
黑客會將一些攻擊指令碼在網頁上通過引誘式點選植入廣告、頁面篡改等,典型的比如
儲存型
儲存型XSS與反射型XSS的差別僅在於,提交的程式碼會儲存到伺服器端(記憶體,資料庫,檔案系統等),下次呼叫的時候就不需要再提交XSS程式碼。
儲存型XSS只能讀取快取或者資料庫了
XSS的防範措施
編碼
對使用者輸入的資料進行HTML Entity編碼
過濾
將使用者輸入的不安全的內容給過濾掉
比如:
移除使用者上傳的DOM屬性,如onerror等
移除使用者上傳的Style節點、Script節點、Iframe節點等
校正
避免直接對HTML Entity解碼
使用DOM Parse轉換,校正不配對的DOM標籤
實戰
構造介面
通過構建Node服務和建立一個評論功能,例項演示XSS的攻擊與預防。
如何構建Node服務我就不重複贅述了,在之前介紹反射型XSS中已經講過,這邊我依然使用這個服務來實戰。
設定個快取
var comments = {};
複製程式碼
編寫一個具有編碼功能的函式
function html_encode(str){
var result = "";
if(str.length==0){
return "";
}
result = str.replace(/&/g,">");
result = result.replace(/</g,"<");
result = result.replace(/>/g,">");
result = result.replace(/\s/g," ");
result = result.replace(/\'/g,"'");
result = result.replace(/\"/g,""");
result = result.replace(/\n/g,"</br>");
return result;
};
複製程式碼
定義一個評論的介面
router.get('/comment', function(req, res, next) {
comments.v = html_encode(req.query.comment);
})
複製程式碼
定義一個使用者拉取評論的介面
router.get('/getComment', function(req, res, next) {
res.json({
comment:comments.v
})
})
複製程式碼
防範措施
ejs
<textarea name="name" rows="8" cols="80" id="text">
<p>sks<img src="null" alt="" onerror="alert(1)" /></p>
</textarea>
<button type="button" name="button" id="btn">評論</button>
<button type="button" name="button" id="get">獲取評論</button>
複製程式碼
<textarea>
用於使用者輸入區域
<button>
用於提交評論和拉取評論資訊
通過這樣,我們就實現了一個模擬的XSS攻擊
js
獲取物件
var btn = document.getElementById("btn");
var get = document.getElementById("get");
var txt = document.getElementById("text");
複製程式碼
新增評論點選事件
btn.addEventListener("click",function(){
......
}
複製程式碼
ajax請求
var xhr = new XMLHttpRequest();
複製程式碼
url
var url = '/comment?comment='+txt.value;
複製程式碼
因為是get請求
開啟物件
在客戶端向服務端傳送之前,首先開啟物件,告訴物件是以GET
方式開啟
xhr.open('GET',url,true);
複製程式碼
定義物件在客戶端響應的方式
xhr.onreadystatechange = function(){
if(xhr.readyState==4){
if(xhr.status==200){
console.log(xhr);
}else{
console.log("error");
}
}
}
複製程式碼
傳送
xhr.send();
複製程式碼
新增請求評論點選事件
get.addEventListener("click",function(){
......
}
複製程式碼
ajax請求
var xhr = new XMLHttpRequest();
複製程式碼
url
var url = '/getComment';
複製程式碼
因為是get請求
開啟物件
在客戶端向服務端傳送之前,首先開啟物件,告訴物件是以GET
方式開啟
xhr.open('GET',url,true);
複製程式碼
定義物件在客戶端響應的方式
-
匯入js
<script src='/public/javascripts/encode.js'></script> <script src='/public/javascripts/domParse.js'></script> 複製程式碼
自行去第三方庫下載
-
定義一個函式
var prase = function(str){ var results = ''; try{ }catch(e){ //TODO handle the exception }finally{ } } 複製程式碼
-
解碼
HTMLParse(he.unescape(str,{strict:true}),{}); 複製程式碼
he
是encode.js
提供的unescape()
對輸入一種反轉義的過程HTMLParse()
在反轉義的基礎上進行domParse,獲得我們能正常使用的結果 -
配對校驗
start:function(tag,attrs,unary){//tag:標籤;attrs:將屬性組成陣列;unary:是否是單標籤 results += '<'+tag; for(int i=0,len=attrs.length;i<len;i++){ results += " "+attrs[i].name+'="'+attrs[i].escaped+'"'; } results += (unary?"/";"")+">"; }, end:function(tag){ results += "</"+tag+">"; }, chars:function(text){ results += text; }, comment:function(text){//註釋 results += "<!--"+text+"-->" } 複製程式碼
檢視一下完整程式碼
<script type="text/javascript"> var prase = function(str){ var results = ''; try{ HTMLParse(he.unescape(str,{strict:true}),{ start:function(tag,attrs,unary){//tag:標籤;attrs:將屬性組成陣列;unary:是否是單標籤 results += '<'+tag; for(int i=0,len=attrs.length;i<len;i++){ results += " "+attrs[i].name+'="'+attrs[i].escaped+'"'; } results += (unary?"/";"")+">"; }, end:function(tag){ results += "</"+tag+">"; }, chars:function(text){ results += text; }, comment:function(text){//註釋 results += "<!--"+text+"-->" } }); return results; }catch(e){ console.log(e); }finally{ } } </script> 複製程式碼
-
定義物件客戶端響應方式
xhr.onreadystatechange = function(){ if(xhr.readyState==4){ if(xhr.status==200){ var com = prase(JSON.parse(xhr.response).comment); }else{ console.log("error"); } } } 複製程式碼
傳送
xhr.send();
複製程式碼
過濾、校正
把獲取的com轉換成DOM節點
var info = document.createElement('span');
info.innerHTML(com);
document.body.appendChild(info);
複製程式碼
重啟一下,開啟瀏覽器
點選評論,然後再點選獲取評論,來模擬瀏覽器載入服務端評論內容的行為
看下怎麼執行的
這邊有p
標籤、sks
文字、img
標籤,在img
標籤裡,有個src
屬性,為null,看下控制檯,報錯:Failed to load resource
,因此觸發了onerror
屬性。
引誘式攻擊
點選評論,再點選獲取評論
點選攻擊我
這就是引誘式攻擊
到此,我們發現,並沒有遮蔽掉XSS攻擊,那是因為我們並沒有進行過濾
過濾
if(tag=='script'||tag=='style'||tag=='link'||tag=='iframe'||tag=='frame'){
return;
}
複製程式碼
這個就是去過濾這些標籤
把之前的程式碼刪掉,因為這段程式碼就包含了那些含有XSS攻擊的指令碼,從而保證我們獲取資訊的安全性,避免XSS指令碼執行的空間。
for(int i=0,len=attrs.length;i<len;i++){
results += " "+attrs[i].name+'="'+attrs[i].escaped+'"';
}
複製程式碼
開啟瀏覽器,重新操作,發現已經成功攔截了XSS攻擊,看下控制檯,我們發現img
標籤下的屬性被自動過濾掉了。