XSS/跨站指令碼攻擊,是一種程式碼注入網頁攻擊,攻擊者可以將程式碼植入到其他使用者都能訪問到的頁面(如論壇、留言板、貼吧等)中。
如今,XSS 攻擊所涉及的場景愈發廣泛。越來越多的客戶端軟體支援 html 解析和 JavaScript 解析,比如:HTML 文件、XML 文件、Flash、PDF、QQ、一些音樂播放器以及瀏覽器的功能介面等。這些使用者經常使用的場景往往都是 XSS 攻擊的高發地帶。
一、XSS攻擊型別
1. 儲存型XSS(持久型)
攻擊者在表單內提交惡意 js 程式碼 ( 如 <script>alert('hello')</script>
),網站後端對提交資料不做任何安全處理,直接儲存在資料庫中。當其他使用者訪問這個已被攻擊的網站,js 程式碼攻擊就會被觸發。這個型別的 XSS 攻擊會儲存在資料庫中,持續時間長,影響範圍大。
2. 反射型XSS(非持久型)
反射型 XSS 攻擊,是正常使用者請求一個非法資源時觸發的攻擊。此型別攻擊通常需要使用者主動去訪問帶攻擊的連結,一旦點開了連結,大概率被成功攻擊(比如:我有一部電影資源,請點選 http://xxxxx 下載)。
3. DOM XSS
基於 DOM 的 XSS 攻擊是反射型攻擊的變種。伺服器正常返回資料,其攻擊在於正常使用者進行某種操作 ( js 操作) 時,觸發攻擊者的 URL 攻擊程式碼,伺服器難以檢測出這是否為非法請求。
二、XSS攻擊的危害
- 掛馬;
- 盜取使用者cookie;
- DoS(拒絕服務)客戶端瀏覽器;
- 釣魚攻擊,高階釣魚技巧;
- 編寫針對性的 XSS 病毒,刪除目標文章、惡意篡改資料、嫁禍;
- 劫持使用者 Web 行為, 進一步滲透內網;
- 爆發 Web2.0 蠕蟲;
- 蠕蟲式的 DDoS 攻擊;
- 蠕蟲式掛馬攻擊、刷廣告、刷流量、破壞網上資料
- ……
三、JavaScript攔截
隨著網際網路發展,XSS 攻擊涉及場景越大,其造成的危害隨之擴大。攔截與防護的關鍵,重中之重當然是從後端入手。然而,這並不意味著網頁前端無需進行相應的攔截。前後端共同攔截,網站應對 XSS 攻擊的防護才會更加周全。接下來,本文將淺析前端的 XSS 攻擊攔截。
XSS 攻擊簡單來說就是程式碼的注入,特指惡意使用者輸入惡意程式程式碼。為了防範這類程式碼的注入,網站需要確保其使用者輸入的安全性。對於攻擊驗證,可以採用以下措施:
- 編碼,即轉義使用者的輸入,把使用者的輸入解讀為資料而不是程式碼;
- 校驗,對使用者的輸入及請求一律進行黑名單過濾檢查,如對特殊字元進行過濾,設定輸入域的匹配規則等,主要針對三類注入:內聯事件及內聯指令碼、靜態指令碼、動態指令碼;
- 建立上報攻擊資訊,對攻擊者攻擊資訊進行分析,增強黑名單。
1. 編碼
在客戶端使用 JavaScript 對使用者輸入進行編碼時,有一些內建的方法和屬性可以在自動感知上下文的情況下,對所有的輸入資料進行編碼。
一些自動編碼的方法可參考下表:
上下文 | 方法 |
---|---|
html元素 | 例:<div>userinput</div> → node.textContent = userInput |
html屬性 | 例:<input value="userInput"/> → node.setAttribute(attr,userInput) |
URL查詢值 | 例:http://example.com/?parameter=userInput → encodeURIComponent(userInput) |
CSS值 | 例:color: userInput → Node.style.property=userInput |
2. 內聯事件及內聯指令碼
一些比較常見的注入方式,大部分都是 javascript:...
及內聯事件 on*
。如:
<a href="javascript:alert('hello')" ></a>
<iframe src="javascript:alert('hello')" />
<img src='x' onerror="alert('hello')" />
<video src='x' onerror="alert('hello')" ></video>
<div onclick="alert('hello')" onmouseover="alert('hello2')" ><div>
類似這種注入,我們需要在瀏覽器觸發點選事件前,對 javascript:...
內容進行黑名單判斷,以實現防護效果。對於 on*
也是一樣,可以使用 addEventListener
防護內聯事件注入:
// 定義 黑名單 策略
var
blackList = [
'xss',
'flow..'
];
// 黑名單匹配
function blackMatch(blackList, value) {
if (!blackList) return false;
for (var i=0; i < blackList.length; i++) {
var reg = new RegExp(blackList[i], 'i');
if (reg.test(value)) {
return true;
} else {
return false;
}
}
document.addEventListener('click', function(e) {
var el = e.target;
//查詢 a標籤 <a href="javascript:">
if (el.tagName === 'A' && el.protocol === 'javascript:') {
var code = el.href.substr(11);
if (blackMatch(blackList, code)) {
//非法輸入
elem.href = 'javascript:void(0)';
}
}
}, true)
3. 靜態指令碼
通常,攻擊者會向頁面中注入一個靜態指令碼連結,比如,注入 <scriptsrc="http://xxx/xss.js">
。對於這類注入,我們需要在指令碼執行前找出可疑指令碼,並銷燬掉。此處可使用 MutationObserver
高階 api,在頁面載入變化時,對靜態指令碼檔案進行監控:
// Mutation 監聽DOM樹變化
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
// 新增節點數
var nodes = mutation.addedNodes;
for (var i = 0; i <= nodes.length - 1; i++) {
var node = nodes[i];
//blacklist 黑名單策略列表
if (blackMatch(blackList, code)) {
node.parentNode.removeChild(node);
console.log('發現可疑模組:', node);
}
}
});
});
// 當頁面元素節點新增和刪除操作會被觀察到
observer.observe(document, {
subtree: true,
childList: true
});
4. 動態指令碼
除上述靜態指令碼外,攻擊者還經常使用動態指令碼注入,而且 MutationObserver
api 無法監聽 DOM 的變化,攻擊指令碼依舊會執行。比如:
var script = document.createElement('script');
script.setAttribute('type', 'text/javascript');
script.setAttribute('src', 'http://xxxxx/xss.js');
document.getElementsByTagName('body')[0].appendChild(script)
這種注入方式,可能需要使用原生的 setAttribute
方法,來監聽 src
屬性的值,再通過黑名單判斷它是否具有合法性,重寫 setAttribute
介面來實現防護:
var old_setAttribute = Element.prototype.setAttribute;
// 重寫 setAttribute 介面
Element.prototype.setAttribute = function(name, value) {
if (this.tagName == 'SCRIPT' && /^src$/i.test(name)) {
// 黑名單判斷
if (blackMatch(blackList, value)) {
console.log('發現可疑模組:', value);
return;
}
}
old_setAttribute.apply(this, arguments);
};
5. 上報攻擊資訊
上述幾種前端防護均需要黑名單庫進行判斷,然而如何才能增強黑名單呢?
我們需要將攻擊者每次的攻擊資訊收集起來,併傳送到伺服器進行分析、處理。這樣一來,不僅可以增強黑名單,還能根據收集到的資訊設計針對性措施。
可定義一個上報函式,使用 node 接收攻擊資訊。其結果如下:
function hijackReport(name, value) {
var img = document.createElement('img'),
hijackName = name,
hijackValue = value.toString(),
curDate = new Date().getTime();
// 上報
img.src = 'http://192.168.1.3:3002/report/?msg=' + hijackName +
'&value=' + hijackValue + '&time=' + curDate;
}
在此,有一個防 XSS 攻擊的庫可供學習瞭解。
Tips - Content Security Policy (CSP)
使用驗證來防止 XSS 攻擊的缺陷在於,只要存在一絲漏洞,就會使網站遭到攻擊,而 Content Security Policy (CSP) 的標準則能夠降低這一風險。
(張傑 | 天存資訊)
Ref
- 《Web前端黑客技術揭祕》 - 鍾晨鳴
- ‘JavaScript防http劫持與XSS’ - shanyezi
- https://github.com/leizongmin/js-xss