基礎
XSS就是讓瀏覽器執行想插入的js。那么如何發現這些漏洞呢?只要有輸入和輸出的地方都伴隨著漏洞的產生,下面介紹基礎的XSS漏洞分析。
XSS攻擊并非一個<sc+ript+>al+ert('xs+s漏洞')</script>(這里請看源碼)語句就可以檢測。
<script>alert('xss漏洞')</script>
XSS初級測試(反射型XSS):
反射型XSS需要欺騙用戶點擊才能觸發XSS代碼。輸入框是否過濾<>’”/等字符,輸入框中輸入這些字符后提交,查看網頁源碼中這四個字符有沒有被過濾;
在輸入框或者url的參數中輸入<img onerror=alert(1) src=1>后,如果出現界面錯誤,那就提BUG吧;
使用eval()函數,該函數執行js代碼,輸入框輸入或者url參數等于eval("alert('Hello world')”),是否能夠執行。
XSS中級測試:
寬字節注入漏洞,GB系列這些編碼格式只有兩個字節,引起的安全問題是吃一個ASCII字符的現象像一個<input type=“text” value=“”> 的語句,我們要做的是閉合雙引號(%22),但是如果value輸入%22的話就會被轉義為%5C%22,如果輸入%C0%22 /><script>alert(1)</script>的話,過濾器發現%22加入轉義(%5C),然而%C0吃掉了%5C,這樣就閉合了前面的雙引號。
存儲型XSS,和前面的反射型XSS不同,是將XSS代碼提交服務器中并存儲下來,其他人在訪問能拿到該字段的頁面就會觸發XSS代碼執行。我找到了一個烏云上淘寶寶貝詳情的存儲型XSS。
測試建議:
必須對所以交互的輸入字符、http請求頭部變量、cookie變量等檢測字符和html關鍵字段;
前端或者客戶端做了過濾還不夠,服務端也要進行過濾;
為了防止存儲型XSS產生,對存入數據庫的字段不光要在輸入字符時進行過濾和檢測,還要在數據庫字段輸出到頁面時進行過濾和檢測;
網上已有的XSS代碼:XSS代碼;
烏云上很多XSS漏洞的真實例子,大家測試之余可以去搜來看看,烏云網站。
深入
XSS攻擊的本質是執行代碼,根據代碼在頁面中的執行環境可分為3種:
1.存儲型XSS
用戶訪問正常的URL,觸發代碼執行。
大部分情況是由于過濾不完全或未轉碼,直接渲染到頁面中,導致代碼執行。
2.DOM型XSS
用戶訪問正常的URL,觸發代碼執行。
前端人員經常把數據埋在Dom節點中,然后通過JS代碼獲取內容,渲染,然后插入到頁面中,導致代碼被執行。
3.反射型XSS
用戶訪問經過特殊構造的URL,觸發代碼執行.
常通過垃圾郵件傳播,郵件內容大多是具有誘惑性的內容,引誘點擊。QQ被盜后,空間說說等經常出現不明鏈接等等。
存儲型XSS漏洞根據代碼執行位置不同,又可分為:
1.HTML存儲型
發表文章,填寫資料,留言等數據提交到后臺,過濾不全,在用戶訪問頁面的時候,惡意內容渲染到頁面中,導致攻擊。
2.CSS存儲型
常見于淘寶店鋪裝修,網頁換膚,背景圖等,這些地方都需要用戶提交CSS樣式,甚至引入外部JS,如果對輸入沒有做嚴格的校驗,直接渲染到頁面,極易產生安全隱患
淘寶商品詳情頁的評論記錄,交易記錄都曾被非法篡改。
攻擊的方式多種多樣,CSS支持unicode,很多漏洞直接采用編碼的方式繞過安全漏洞。
<div style="width:expression(if(!window.x){alert(1);window.x=1})"></div>
<link rel=stylesheet href=data:,*%7bx:expression(if(!window.x)%7balert(1);window.x=1%7d)%7d />
<div style="width:expression(if(!window.x){alert(1);window.x=1})"></div>
<table background="javascript:alert(/xss/)"></table>'
3.JS存儲型
這種比較少見,有時候會前端會使用eval,jQuery.globalEval,前者在代碼中比較常見,1688這邊,去年安全部集中處理過一批這些漏洞。
用戶一般難以直接輸入JS代碼,但有時候,前端開發人員會要求將數據直接轉成JS代碼,放入到頁面中,方便獲取,如果內容未經過安全處理,攻擊者就會嘗試中斷正常的代碼邏輯,插入攻擊代碼。
Dom型XSS攻擊
'''
前端開發者,經常將數據存放在Dom節點,在某個時候,獲取節點的數據,然后插入到頁面中,插入到頁面時,常使用jQuery('body').html('<script>alert(3)</script>') 操作,導致代碼被執行。
反射型XSS攻擊
用戶訪問經過特殊構造的URL,URL參數中會攜帶攻擊代碼。
現在我們假設一種攻擊場景:私信
- 攻擊者A,利用私信功能向用戶B發送私信,私信內容存在攻擊代碼(JS)
- 用戶瀏覽私信,瀏覽的過程中,JS代碼在同域名下,毫無顧忌地執行,相當于開發方自己的代碼
- 用戶B成為受害者
- 攻擊代碼可以將受害者B的其它私信提交到自己的內容收集平臺
- 攻擊代碼自動與好友發送私信,私信內容與上述一樣。從而產生新的受害者。。。。
- 如此循環
可以看到,用戶B的私密消息,完全被暴露。
很多網站產品會讓用戶填寫個人資料,身份證,姓名等等,一旦發生此類攻擊,用戶的信息也就被泄露了。
目前網站依賴token做CSRF校驗,其有效的前提是不存在XSS漏洞,token不會泄露。
防御
在編寫代碼的時候,盡量站在攻擊者角度去編寫代碼。
其實記住兩點就行:
不要信任用戶輸入的任何數據
輸入、輸出過濾
作為防御方,我們盡量夠做的其實很有限,不同的場景,可能有不同的漏洞產生,提高自身的安全意識,編寫安全代碼是我們目前能夠做的。
舉個小例子,判斷一個URL是否是1688域名,在前端提交的時候會做判斷,后臺也會做判斷。
正則可能如下:
/http://.*.1688.com/i
大家可以思考一下這個正則有問題嗎? 前端可能寫對了,后臺開發人員呢?
在攻擊發生時,往往是多個類型XSS互相利用,最后產生較大的安全問題。還有就是新技術的應用,比如NodeJs,在安全方面的處理還是不夠,這種在線上也存在相同的問題。目前也有新的CSP策略,在高級瀏覽器(IE10,)中可以起到一定的安全防御作用,但是就當前環境來說,低版本瀏覽器,我們還沒法放棄。
一些例子
2011年,新浪微博XSS蠕蟲事件:攻擊者利用廣場的一個反射性XSS URL,自動發送微博、私信,私信內容又帶有該XSS URL,導致病毒式傳播。
09年twitter也發生過類似事件。
烏云平臺也有很多此類例子,可以搜索關鍵詞XSS
- 安全平臺自己也有漏洞 ,rank 10,http://wooyun.org/bugs/wooyun-2010-059832
- 阿里云賬號泄露 ,rank 20, http://wooyun.org/bugs/wooyun-2010-054102
- 騰訊微博 http://www.wooyun.org/bugs/wooyun-2010-020167
- 百度貼吧 http://www.wooyun.org/bugs/wooyun-2010-053221 太多了。。。。。。
有時候,漏洞的危害性可能不是很大,但是極易引起用戶反彈,公關問題,。
實踐 -- show me the code
前端XSS
前端XSS往往由于前端層面用innerHTML或者$('#id').html('...')方法時,沒有對用戶輸入的內容和正常的HTML代碼進行隔離,從而讓黑客可以輕松獲取JS執行權限。
那么,標準的前端邏輯應該怎么寫?
$('#id').html('');
如果我們使用的是模板,例如handbar,由于handbar默認會進行轉義,因此默認即是安全的:

理論上,我們建議能使用模板的場景盡量使用模板,能為我們節省大量的代碼量,并且同時也能保證我們代碼不會出現XSS漏洞。
對于使用underscore的同學,請留意下默認的是不轉義的,例如:
hello: <%= name %>
如果需要轉義,請務必使用:
hello: <%- name %>
注:我們對underscore進行了改造,將這兩個標識進行了翻轉,經過改造后,也符合了默認轉義的原則。
成果
在這之外,我們還有什么手段解決前端XSS問題呢?
jQuery目前已經無孔不入,幾乎找不著沒有使用jQuery的業務場景。
我們的方案是在外部對jQuery進行了hack,將所有html代碼進行安全過濾,以實現前端防火墻的功能。
過濾邏輯如下:將所有js代碼過濾為無效代碼, 例如:
'''
<img src="" onerror="alert(1)"> <script>alert(1)</script>
'''
會替換為:
'''
<img src="" onerror0="alert(1)"> <script0>alert(1)</script>
'''
經過此過濾,XSS就再也無法猖狂了!
并且,此方案還有1個優點,由于此方案是全站底層進行防火部署,因此過濾算法可以隨時進行優化升級,以解決今后出現新的XSS漏洞。
不過,此方案并非沒有缺陷。由于此方案屏蔽了所有的JS代碼,因此對于舊邏輯中所有HTML和JS混合的場景都需要進行改造。
但是,根據實際部署過程來看,這種混合場景占比非常非常少。
我如何部署?
security.js
'''js
(function (win) {
var security = {}; // 初始事件正則
var objOnEvents = {};
var reAllEvents; // init all events
var doc = document;
var arrDoms = [window, doc.createElement("form")];
try {
arrDoms.push(doc.createElement("img"));
arrDoms.push(doc.createElement("iframe"));
arrDoms.push(doc.createElement("object"));
arrDoms.push(doc.createElement("embed"));
arrDoms.push(doc.createElement("audio"));
} catch (e) {
}
var dom;
var key;
for (var i = 0, c = arrDoms.length; i < c; i++) {
dom = arrDoms[i];
for (key in dom) {
if (/^on/.test(key)) {
objOnEvents[key.substring(2)] = 1;
}
}
}
var arrAllEvents = [];
for (key in objOnEvents) {
arrAllEvents.push(key);
}
if (arrAllEvents.length > 0) {
reAllEvents = new RegExp('(['"\\s\\/]on(?:' + arrAllEvents.join('|') + '))\\s*=', 'ig'); } else { reAllEvents = /(['"
\s/]on(\w+))\s=/ig;
} // HTML轉義
security.encodeHTML = function (str) {
return String(str).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '
').replace(/</g, '<').replace(/>/g, '>');
}; // 過濾html中可能引發XSS的代碼
security.htmlFilter = function (str, args) {
var reBadTags = /<(script|link|frame)([^\w])/ig;
var reBadAttrs = /['"\s\/](srcdoc)\s*=/ig; var reBadSrc = /[\'"
\s/]src\s=\s['"]?\s(javascript:|data\s:\stext/html)/ig;
str = String(str); // 過濾script & link...
str = str.replace(reBadTags, '<$10$2'); // 過濾on事件
str = str.replace(reAllEvents, '$10='); // 過濾srcdoc屬性
str = str.replace(reBadAttrs, ' $10='); // 過濾危險src: javascript: data:
str = str.replace(reBadSrc, ' src0="');
return str;
}; // hack jQuery
security.hookJquery = function ($) {
if ($ && !$.fn.rawHtml) { // 1. html->rawHtml
$.fn.rawHtml = $.fn.html;
$.fn.html = function (value) {
var args = Array.prototype.slice.call(arguments);
if (typeof value === 'string' && $.htmlFilter) {
args[0] = security.htmlFilter(value, arguments);
}
return $.fn.rawHtml.apply(this, args);
};
}
};
win.security = security;
})(window);
'''
test.html
''' html
<div id="test"></div>
<script type="text/javascript" src="js/jquery-1.10.2.js"></script>
<script type="text/javascript" src="security.js"></script>
<script type="text/javascript">
security.hookJquery($); $.htmlFilter = true; $('#test').html('<img src="" onerror="alert(1)">')
</script>
框架底層可以直接將jQuery進行hook,但是具體過濾邏輯是否開啟取決于$.htmlFilter是否打開。
在部署過程中,請務必要保證業務代碼全部改造為純HTML的情況下才能開啟開關,否則會造成線上故障