摘要:分析及預(yù)防,又稱跨站腳本,的重點不在于跨站點,而是在于腳本的執(zhí)行。在這里需要強調(diào)一點的是,默認(rèn)會禁止代碼塊的執(zhí)行禁止內(nèi)聯(lián)事件處理函數(shù)禁止內(nèi)聯(lián)樣式禁止和。
XSS分析及預(yù)防
XSS(Cross Site Scripting),又稱跨站腳本,XSS的重點不在于跨站點,而是在于腳本的執(zhí)行。在WEB前端應(yīng)用日益發(fā)展的今天,XSS漏洞尤其容易被開發(fā)人員忽視,最終可能造成對個人信息的泄漏。如今,仍然沒有統(tǒng)一的方式來檢測XSS漏洞,但是對于前端開發(fā)人員而言,仍是可以在某些細(xì)微處避免的,因此本文會結(jié)合筆者的學(xué)習(xí)和經(jīng)驗總結(jié)解決和避免的一些方案,并簡要從webkit內(nèi)核分析瀏覽器內(nèi)核對于XSS避免所做的努力,了解底層基礎(chǔ)設(shè)施對預(yù)防XSS所做的貢獻(xiàn)。
XSS的種類和特點此處不詳細(xì)講解XSS的一些細(xì)節(jié)
XSS的目標(biāo)是讓其他站點的js文件運行在目標(biāo)站點的上,這主要發(fā)生在頁面渲染階段。在該階段發(fā)生了某些非預(yù)期的腳本行為,該腳本可能來自用戶的輸入,也可能來自域外的其他js文件,不一而足。XSS的發(fā)生起源來自于用戶輸入,因此XSS根據(jù)用戶輸入數(shù)據(jù)以何種形式、何時觸發(fā)XSS、是否有后端服務(wù)器的參與劃分為三種類型,分別是反射型XSS、持久型XSS和DOM XSS。
反射型XSS反射型XSS,顧名思義在于“反射”這個一來一回的過程。反射型XSS的觸發(fā)有后端的參與,而之所以觸發(fā)XSS是因為后端解析用戶在前端輸入的帶有XSS性質(zhì)的腳本或者腳本的data URI編碼,后端解析用戶輸入處理后返回給前端,由瀏覽器解析這段XSS腳本,觸發(fā)XSS漏洞。因此如果要避免反射性XSS,則必須需要后端的協(xié)調(diào),在后端解析前端的數(shù)據(jù)時首先做相關(guān)的字串檢測和轉(zhuǎn)義處理;同時前端同樣也許針對用戶的數(shù)據(jù)做excape轉(zhuǎn)義,保證數(shù)據(jù)源的可靠性。
e.x.
localhost/test.php
如果通過
localhost/test.php?name=
訪問頁面,那么經(jīng)過后端服務(wù)器的處理,就會造成反射性XSS的發(fā)生。
同理,通過傳入data uri編碼的字符串也會導(dǎo)致XSS,如
localhost/test.php?name=data:text/html;charset=utf-8;base64,PHNjcmlwdD5hbGVydChkb2N1bWVudC5jb29raWUpPC9zY3JpcHQ+
會導(dǎo)致同樣的問題。該段編碼的字串解碼后是“”。
持久型XSS仍然需要服務(wù)端的參與,它與反射型XSS的區(qū)別在于XSS代碼是否持久化(硬盤,數(shù)據(jù)庫)。反射型XSS過程中后端服務(wù)器僅僅將XSS代碼保存在內(nèi)存中,并為持久化,因此每次觸發(fā)反射性XSS都需要由用戶輸入相關(guān)的XSS代碼;而持久型XSS則僅僅首次輸入相關(guān)的XSS代碼,保存在數(shù)據(jù)庫中,當(dāng)下次從數(shù)據(jù)庫中獲取該數(shù)據(jù)時在前端未加字串檢測和excape轉(zhuǎn)碼時,會造成XSS,而且由于該漏洞的隱蔽性和持久型的特點,在多人開發(fā)的大型應(yīng)用和跨應(yīng)用間的數(shù)據(jù)獲取時造成的大范圍的XSS漏洞,危害尤其大。這就需要開發(fā)人員培養(yǎng)良好的WEB前端安全意識,不僅僅不能相信用戶的輸入,也不能完全相信保存在數(shù)據(jù)庫中的數(shù)據(jù)(即后端開發(fā)人員忽視的數(shù)據(jù)安全檢測)。針對持久型XSS沒有好的解決方式,只能由開發(fā)人員保證。當(dāng)然規(guī)則是由開發(fā)者制定,如果忽略用戶體驗的話,可以制定一套嚴(yán)謹(jǐn)?shù)妮斎胍?guī)則,對相關(guān)關(guān)鍵詞和輸入類型(如data URI檢測,禁止輸入)的檢測和禁止,盡可能規(guī)避用戶發(fā)現(xiàn)XSS漏洞的可能性,從源頭處理。
DOM XSSDOM XSS完全在前端瀏覽器觸發(fā),無需服務(wù)端的參與,因此這是前端開發(fā)工程師的“地盤”,理應(yīng)獲得我們的關(guān)注。
e.x.
localhost/test.html
如果訪問localhost/test.html#document.cookie ,那么就會觸發(fā)最簡單的危害非常大的DOM XSS。它完全沒有服務(wù)端的參與,僅僅由用戶的輸入和不安全的腳本執(zhí)行造成,當(dāng)然在本例中僅僅是最簡單的情況,如果用戶輸入字符串‘’或者text/html格式的data URI,則更難檢測,也危害更大,黑客操作起來更為容易。
因此預(yù)防DOM XSS,需要前端開發(fā)人員警惕用戶所有的輸入數(shù)據(jù),做到數(shù)據(jù)的excape轉(zhuǎn)義,同時盡可能少的直接輸出HTML的內(nèi)容;不用eval、new Function、setTimeout等較為hack的方式解析外站數(shù)據(jù)和執(zhí)行js腳本;禁止內(nèi)聯(lián)事件處理函數(shù)“”;如果在考慮安全性的前提下需要獲取外站腳本的執(zhí)行結(jié)果,可以采用前端沙盒(建立空的iframe執(zhí)行腳本,該iframe無法操作當(dāng)前文檔對象模型)、worker線程的方式完成,保證DOM的安全。
XSS預(yù)防XSS漏洞難以檢測,但是為了WEB安全仍需要盡力避免,在本節(jié)將會針對三種類型XSS漏洞提出對應(yīng)解決方法,并從其他角度提供更具啟發(fā)性的意見。
針對反射型XSS,在對應(yīng)的小節(jié)中也提到過,需要服務(wù)端和前端共同預(yù)防,針對用戶輸入的數(shù)據(jù)做解析和轉(zhuǎn)義,對于前端開發(fā)而言,則是善于使用escape,針對data URI內(nèi)容做正則判斷,禁止用戶輸入非顯示信息,如MIME類型為“text/html,text/plain”類型的內(nèi)容。
對于存儲型XSS,處理方式仍然類同于反射性XSS。
對于DOM XSS,則需要慎之又慎。由于造成XSS的原因在于用戶的輸入,因此在前端,需要特別注意以下的用戶輸入源:
document.URL, location.hash, location.research, document.referrer(此處應(yīng)尤為注意,referrer屬性雖然可用于避免CSRF,但可觸發(fā)XSS攻擊), XHR返回值(跨域返回值), form表單及各種input框
針對以上輸入源,需要做相對于的檢測和轉(zhuǎn)義。在以上輸入源中獲取數(shù)據(jù)后,可能會有各種DOM操作或純粹的js計算,這些操作則是真正觸發(fā)XSS的罪魁禍?zhǔn)祝?/p>
1,直接輸出HTML內(nèi)容 document.body.innerHTML = ... document.body.outterHTML = ... document.write() 2,HTML標(biāo)簽內(nèi)聯(lián)腳本 3,直接執(zhí)行腳本 eval new Function(){} setTimeout() window.execScript() 4,打開新頁面觸發(fā)XSS(包括反射型XSS和持久型XSS) window.open() location.href = ... location.hash = ...
在操作DOM時,需要尤其注意上述操作,針對可能造成的XSS需要進行字串轉(zhuǎn)義。當(dāng)然,有些操作是完全可以避免的:對于innerHTML的拼接操作,需要摒棄jQuery式的鏈?zhǔn)讲僮鞫褂们岸四0嫒鏰rtTemplate,也可選擇使用由后端渲染好的可靠的數(shù)據(jù),這樣既保證性能也確保安全;對于HTML標(biāo)簽內(nèi)嵌js,則需要完全避免,這是一種容錯率很低的實現(xiàn);直接執(zhí)行腳本和解析數(shù)據(jù),則需避免eval和new Funciton等操作,改為JSON.parse、iframe沙盒和webWorker執(zhí)行;而針對打開新頁面觸發(fā)的XSS則需要開發(fā)人員自行把控。
另外的嘗試上文提到的僅僅是對應(yīng)的XSS避免方案,但是如果將目光放置在全局,站在瀏覽器的角度上,則會變的更為柳暗花明。現(xiàn)階段,大多數(shù)瀏覽器都支持多種安全策略,如沙盒機制,跨域機制,跨文檔消息和CSP。在這里,我們關(guān)注CSP(Content Security Policy),又稱內(nèi)容安全協(xié)議,CSP通過服務(wù)端響應(yīng)的HTTP頭部來制定網(wǎng)頁相關(guān)資源的加載域,這些資源限定于js文件、css文件、image、iframe、字體和其他對象(如object、applet)。
CSP通過HTTP頭部由服務(wù)端制定,頭部類型由于歷史原因總共由三種,這三種僅僅是兼容性的差別,針對chrome瀏覽器,我們僅需關(guān)注Content-Security-Policy頭部。CSP頭部的定義規(guī)則如下:
Content-Security-Policy: 名 值; 名 值; 名 值;
具體的指令名如下圖:
指令值的規(guī)范如下圖:
因此,如果我們要避免XSS攻擊,可以限定腳本的來源域,如:
Content-Security-Policy: default-src "self" ajax.googleapis.com;
這樣,非本域和ajax.googleapis.com域下的其他腳本不會被加載,避免了XSS。
在這里需要強調(diào)一點的是,默認(rèn)CSP會禁止script代碼塊的執(zhí)行;禁止內(nèi)聯(lián)事件處理函數(shù);禁止內(nèi)聯(lián)樣式;禁止eval和new Function。對于內(nèi)聯(lián)script代碼塊和內(nèi)聯(lián)樣式,可通過CSP的header設(shè)置,如Content-Security-Policy: default-src "self"; script-src "unsafe-inline";。
CSP有一個指令需要注意,即report-uri,它會將錯誤信息主動發(fā)送至改cgi(sevlet),用于管理員的統(tǒng)一管控。report-uri屬性將會在下文中涉及到。
webkit中的XSS組件XSS攻擊主要發(fā)生在頁面的渲染時,當(dāng)瀏覽器的渲染引擎獲取到該頁面并開始解析時,是可以在該階段進行安全校驗的,具體的時間節(jié)點則是在詞法分析后針對每個token做過濾。
在webkit中,由HTMLDocumentParser解析得到token后,使用XSSAuditor進行過濾,具體則是在filterToken中執(zhí)行,不僅僅是針對token的名稱,其屬性也是監(jiān)測重點。在webkit中采用黑名單機制,針對“,