基礎(chǔ)
XSS就是讓瀏覽器執(zhí)行想插入的js垄琐。那么如何發(fā)現(xiàn)這些漏洞呢?只要有輸入和輸出的地方都伴隨著漏洞的產(chǎn)生惯悠,下面介紹基礎(chǔ)的XSS漏洞分析洲胖。
XSS攻擊并非一個<sc+ript+>al+ert('xs+s漏洞')</script>(這里請看源碼)語句就可以檢測。
<script>alert('xss漏洞')</script>
XSS初級測試(反射型XSS):
反射型XSS需要欺騙用戶點擊才能觸發(fā)XSS代碼蛛芥。輸入框是否過濾<>’”/等字符提鸟,輸入框中輸入這些字符后提交军援,查看網(wǎng)頁源碼中這四個字符有沒有被過濾仅淑;
在輸入框或者url的參數(shù)中輸入<img onerror=alert(1) src=1>后,如果出現(xiàn)界面錯誤胸哥,那就提BUG吧涯竟;
使用eval()函數(shù),該函數(shù)執(zhí)行js代碼空厌,輸入框輸入或者url參數(shù)等于eval("alert('Hello world')”)庐船,是否能夠執(zhí)行。
XSS中級測試:
寬字節(jié)注入漏洞嘲更,GB系列這些編碼格式只有兩個字節(jié)筐钟,引起的安全問題是吃一個ASCII字符的現(xiàn)象像一個<input type=“text” value=“”> 的語句,我們要做的是閉合雙引號(%22)赋朦,但是如果value輸入%22的話就會被轉(zhuǎn)義為%5C%22篓冲,如果輸入%C0%22 /><script>alert(1)</script>的話李破,過濾器發(fā)現(xiàn)%22加入轉(zhuǎn)義(%5C),然而%C0吃掉了%5C壹将,這樣就閉合了前面的雙引號嗤攻。
存儲型XSS,和前面的反射型XSS不同诽俯,是將XSS代碼提交服務(wù)器中并存儲下來妇菱,其他人在訪問能拿到該字段的頁面就會觸發(fā)XSS代碼執(zhí)行。我找到了一個烏云上淘寶寶貝詳情的存儲型XSS暴区。
測試建議:
必須對所以交互的輸入字符闯团、http請求頭部變量、cookie變量等檢測字符和html關(guān)鍵字段仙粱;
前端或者客戶端做了過濾還不夠偷俭,服務(wù)端也要進行過濾;
為了防止存儲型XSS產(chǎn)生缰盏,對存入數(shù)據(jù)庫的字段不光要在輸入字符時進行過濾和檢測涌萤,還要在數(shù)據(jù)庫字段輸出到頁面時進行過濾和檢測;
網(wǎng)上已有的XSS代碼:XSS代碼口猜;
烏云上很多XSS漏洞的真實例子负溪,大家測試之余可以去搜來看看,烏云網(wǎng)站济炎。
深入
XSS攻擊的本質(zhì)是執(zhí)行代碼川抡,根據(jù)代碼在頁面中的執(zhí)行環(huán)境可分為3種:
1.存儲型XSS
用戶訪問正常的URL,觸發(fā)代碼執(zhí)行须尚。
大部分情況是由于過濾不完全或未轉(zhuǎn)碼崖堤,直接渲染到頁面中,導(dǎo)致代碼執(zhí)行耐床。
2.DOM型XSS
用戶訪問正常的URL密幔,觸發(fā)代碼執(zhí)行。
前端人員經(jīng)常把數(shù)據(jù)埋在Dom節(jié)點中撩轰,然后通過JS代碼獲取內(nèi)容胯甩,渲染,然后插入到頁面中堪嫂,導(dǎo)致代碼被執(zhí)行偎箫。
3.反射型XSS
用戶訪問經(jīng)過特殊構(gòu)造的URL,觸發(fā)代碼執(zhí)行.
常通過垃圾郵件傳播皆串,郵件內(nèi)容大多是具有誘惑性的內(nèi)容淹办,引誘點擊。QQ被盜后恶复,空間說說等經(jīng)常出現(xiàn)不明鏈接等等怜森。
存儲型XSS漏洞根據(jù)代碼執(zhí)行位置不同齐遵,又可分為:
1.HTML存儲型
發(fā)表文章,填寫資料塔插,留言等數(shù)據(jù)提交到后臺梗摇,過濾不全,在用戶訪問頁面的時候想许,惡意內(nèi)容渲染到頁面中伶授,導(dǎo)致攻擊。
2.CSS存儲型
常見于淘寶店鋪裝修流纹,網(wǎng)頁換膚糜烹,背景圖等,這些地方都需要用戶提交CSS樣式漱凝,甚至引入外部JS疮蹦,如果對輸入沒有做嚴格的校驗,直接渲染到頁面茸炒,極易產(chǎn)生安全隱患
淘寶商品詳情頁的評論記錄愕乎,交易記錄都曾被非法篡改。
攻擊的方式多種多樣壁公,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代碼芳绩,但有時候,前端開發(fā)人員會要求將數(shù)據(jù)直接轉(zhuǎn)成JS代碼撞反,放入到頁面中妥色,方便獲取,如果內(nèi)容未經(jīng)過安全處理痢畜,攻擊者就會嘗試中斷正常的代碼邏輯垛膝,插入攻擊代碼。
Dom型XSS攻擊
'''
前端開發(fā)者丁稀,經(jīng)常將數(shù)據(jù)存放在Dom節(jié)點,在某個時候倚聚,獲取節(jié)點的數(shù)據(jù)线衫,然后插入到頁面中,插入到頁面時惑折,常使用jQuery('body').html('<script>alert(3)</script>') 操作授账,導(dǎo)致代碼被執(zhí)行枯跑。
反射型XSS攻擊
用戶訪問經(jīng)過特殊構(gòu)造的URL,URL參數(shù)中會攜帶攻擊代碼白热。
現(xiàn)在我們假設(shè)一種攻擊場景:私信
- 攻擊者A敛助,利用私信功能向用戶B發(fā)送私信,私信內(nèi)容存在攻擊代碼(JS)
- 用戶瀏覽私信屋确,瀏覽的過程中纳击,JS代碼在同域名下,毫無顧忌地執(zhí)行攻臀,相當于開發(fā)方自己的代碼
- 用戶B成為受害者
- 攻擊代碼可以將受害者B的其它私信提交到自己的內(nèi)容收集平臺
- 攻擊代碼自動與好友發(fā)送私信焕数,私信內(nèi)容與上述一樣。從而產(chǎn)生新的受害者刨啸。堡赔。。设联。
- 如此循環(huán)
可以看到善已,用戶B的私密消息,完全被暴露离例。
很多網(wǎng)站產(chǎn)品會讓用戶填寫個人資料雕拼,身份證,姓名等等粘招,一旦發(fā)生此類攻擊啥寇,用戶的信息也就被泄露了。
目前網(wǎng)站依賴token做CSRF校驗洒扎,其有效的前提是不存在XSS漏洞辑甜,token不會泄露。
防御
在編寫代碼的時候袍冷,盡量站在攻擊者角度去編寫代碼磷醋。
其實記住兩點就行:
不要信任用戶輸入的任何數(shù)據(jù)
輸入、輸出過濾
作為防御方胡诗,我們盡量夠做的其實很有限邓线,不同的場景,可能有不同的漏洞產(chǎn)生煌恢,提高自身的安全意識骇陈,編寫安全代碼是我們目前能夠做的。
舉個小例子瑰抵,判斷一個URL是否是1688域名你雌,在前端提交的時候會做判斷,后臺也會做判斷二汛。
正則可能如下:
/http://.*.1688.com/i
大家可以思考一下這個正則有問題嗎? 前端可能寫對了婿崭,后臺開發(fā)人員呢拨拓?
在攻擊發(fā)生時,往往是多個類型XSS互相利用氓栈,最后產(chǎn)生較大的安全問題渣磷。還有就是新技術(shù)的應(yīng)用,比如NodeJs,在安全方面的處理還是不夠授瘦,這種在線上也存在相同的問題醋界。目前也有新的CSP策略,在高級瀏覽器(IE10,)中可以起到一定的安全防御作用奥务,但是就當前環(huán)境來說物独,低版本瀏覽器,我們還沒法放棄氯葬。
一些例子
2011年挡篓,新浪微博XSS蠕蟲事件:攻擊者利用廣場的一個反射性XSS URL,自動發(fā)送微博帚称、私信官研,私信內(nèi)容又帶有該XSS URL,導(dǎo)致病毒式傳播闯睹。
09年twitter也發(fā)生過類似事件戏羽。
烏云平臺也有很多此類例子,可以搜索關(guān)鍵詞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 太多了始花。。孩锡。酷宵。。躬窜。
有時候浇垦,漏洞的危害性可能不是很大,但是極易引起用戶反彈荣挨,公關(guān)問題男韧,。
實踐 -- show me the code
前端XSS
前端XSS往往由于前端層面用innerHTML或者$('#id').html('...')方法時默垄,沒有對用戶輸入的內(nèi)容和正常的HTML代碼進行隔離此虑,從而讓黑客可以輕松獲取JS執(zhí)行權(quán)限。
那么厕倍,標準的前端邏輯應(yīng)該怎么寫寡壮?
$('#id').html('');
如果我們使用的是模板,例如handbar讹弯,由于handbar默認會進行轉(zhuǎn)義况既,因此默認即是安全的:

理論上,我們建議能使用模板的場景盡量使用模板组民,能為我們節(jié)省大量的代碼量棒仍,并且同時也能保證我們代碼不會出現(xiàn)XSS漏洞。
對于使用underscore的同學(xué)臭胜,請留意下默認的是不轉(zhuǎn)義的莫其,例如:
hello: <%= name %>
如果需要轉(zhuǎn)義,請務(wù)必使用:
hello: <%- name %>
注:我們對underscore進行了改造耸三,將這兩個標識進行了翻轉(zhuǎn)乱陡,經(jīng)過改造后,也符合了默認轉(zhuǎn)義的原則仪壮。
成果
在這之外憨颠,我們還有什么手段解決前端XSS問題呢?
jQuery目前已經(jīng)無孔不入积锅,幾乎找不著沒有使用jQuery的業(yè)務(wù)場景爽彤。
我們的方案是在外部對jQuery進行了hack,將所有html代碼進行安全過濾缚陷,以實現(xiàn)前端防火墻的功能适篙。
過濾邏輯如下:將所有js代碼過濾為無效代碼, 例如:
'''
<img src="" onerror="alert(1)"> <script>alert(1)</script>
'''
會替換為:
'''
<img src="" onerror0="alert(1)"> <script0>alert(1)</script>
'''
經(jīng)過此過濾,XSS就再也無法猖狂了箫爷!
并且嚷节,此方案還有1個優(yōu)點,由于此方案是全站底層進行防火部署虎锚,因此過濾算法可以隨時進行優(yōu)化升級硫痰,以解決今后出現(xiàn)新的XSS漏洞。
不過翁都,此方案并非沒有缺陷碍论。由于此方案屏蔽了所有的JS代碼,因此對于舊邏輯中所有HTML和JS混合的場景都需要進行改造柄慰。
但是鳍悠,根據(jù)實際部署過程來看,這種混合場景占比非常非常少坐搔。
我如何部署藏研?
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轉(zhuǎn)義
security.encodeHTML = function (str) {
return String(str).replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(//g, '
').replace(/</g, '<').replace(/>/g, '>');
}; // 過濾html中可能引發(fā)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是否打開概行。
在部署過程中蠢挡,請務(wù)必要保證業(yè)務(wù)代碼全部改造為純HTML的情況下才能開啟開關(guān),否則會造成線上故障