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