使用腳本隔離技術對抗XSS攻擊
概述
在聖誕節之後,我與@mikewest和@fgrx討論了關於使用「隔離」的概念(將會在下面解釋)作為對抗XSS漏洞攻擊的安全緩解措施。這看起來似乎是一個很有趣的問題,因此,我花了一些時間研究了一下。
下面描述的設計方案將允許你(web開發人員)通過兩個簡單的步驟保護一些客戶端免受XSS攻擊:
設置新的cookie標誌(isolatedScript = true)
設置一個HTTP標頭(Isolated-Script:true)
就這樣。有了這兩個東西,你就可以保護你的應用程序防禦XSS漏洞攻擊。它有點類似於Suborigins,但是使用的是「隔離腳本」的思路防禦XSS攻擊,即使js文件是在同一個頁面內!
除此之外,它還有另外一個優點,它也能夠緩解第三方JavaScript代碼(如廣告,分析,etcetera)。
設計
隔離腳本包括以下三個要素,它們一起工作以提供隔離腳本的防禦能力。
(譯者註:為了讓讀者更易理解本文所提供的思路且保證原文純正的feel,所以譯者對這三個要素名詞不作翻譯)
Isolated Worlds
Isolated cookiesSecret HTML
我會在下面詳細描述每一個要素,並給你一些演示。
Isolated Worlds是當前在Chrome擴展的用戶腳本和Firefox中的Greasemonkey腳本中最突出使用的一個概念 —— 本質上,它允許JavaScript代碼通過文檔的DOM獲得「view(視圖)」,但是是在一個被隔離的JavaScript運行時環境中。 讓我詳細的來解釋一下:
假設在一個網站上有如下代碼:
Isolated World將有權訪問document.getElementById(」 text」),但它不能夠訪問window.variable這個變數。 也就是說,兩個腳本共享的唯一的東西就是HTML的DOM的獨立視圖。 這種隔離非常重要,因為用戶腳本可能會提升許可權,例如,他們可以向任何網站發起XMLHttpRequests請求並讀取其響應內容。
如果不使用Isolated World進行隔離,那麼頁面就可以在用戶的腳本中執行代碼,並最終攻擊用戶:
document.getElementById = function() { arguments.callee.caller.constructor("attack()")(); };
通過這樣的方式,Isolated World就允許我們從攻擊者執行的上下文中保護我們的特權執行上下文。那麼,問題來了:我們能否可以使用相同的設計來保護受信任的腳本防止XSS漏洞嗎?
也就是說,我們與其專註於阻止腳本執行作為一種緩解措施(我們發現這種措施容易出問題)還不如將關注點放在將可信腳本與不可信腳本隔離開來。
在同一個DOM中運行具有特權和非特權腳本的想法並不新鮮,事實上,已經有一些這方面的具體實現(如Mario Heiderich的Iceshield和Antoine Delignat-Lavaud的Defensive JS),但是它們的實現都需要將代碼重寫。使用Isolated World,就沒有那麼麻煩。
因此,隔離腳本的思路就是—— 為Web開發人員提供一種機制,將特定腳本標記為受信任的,然後由瀏覽器將它們載入後在Isolated World中運行。
要在demo中實現這一點的一個簡單方法就是通過在Chrome擴展中重用Isolated Worlds實現,通過在頁面中插入一個用戶腳本,為每個腳本設置正確的HTTP響應頭。
(譯者註:被隔離的腳本就是可信任的腳本,而不是惡意腳本文件。)
Isolated Cookies
現在我們有一個腳本運行在受信任的執行上下文中,現在我們需要一種方法能夠讓Web伺服器識別來自這個腳本的請求。這個做法很有必要,因為伺服器可能只想將一些敏感數據暴露給被隔離的腳本。
最簡單的方法是添加一個類似於Origin頭的新的HTTP請求頭(我們可以使用Isolated-Script:foo.js)。另一種方法是創建一種新的cookie類型,它只在請求來自於一個被隔離的腳本時才會發送給伺服器。此替代方法優於HTTP請求頭有兩個原因,如下:
向後兼容,沒有實現這種方法的瀏覽器將只是像往常一樣發送cookie。
它可以與同一網站的Cookie(同時也能防禦CSRF)結合使用。
Web伺服器需要設置Cookie信息:
Set-Cookie: SID=XXXX; httpOnly; secure; SameSite=Strict; isolatedScript
然後,瀏覽器會像往常一樣處理cookie,只有當請求是由被隔離的腳本發出的才會在請求信息中包含該Cookie,另外,如果瀏覽器不能識別這個新的標誌,將會在所有的請求信息中包含該Cookie。
要在demo中實現這一點的一個簡單方法是,在cookie中使用特殊的名稱,並且除非請求來自於被隔離的腳本否則拒絕發送cookie。
Devdatta提出了一個想法是使用cookie前綴,這同樣也可以保護cookie免受竄改攻擊。
Secret HTML
我們現在的這種針對腳本隔離的機制依舊讓腳本有一些額外的特權,並讓這些腳本在隔離的執行上下文中運行來阻止惡意代碼執行,然而,該腳本如果將想要顯示給用戶的數據寫入到 DOM後,那麼惡意的腳本也能夠立即讀取到這些內容。所以,為此,我們需要為將被隔離的腳本讀寫的HTML 內容進行保護從而讓惡意的腳本不能夠讀取到。
雖然此原語你可能從未聽過,但它實際上在今天已經存在了!JavaScript代碼本身已經可以編寫用戶可見的HTML內容,但如何讓這些HTML內容對DOM不可用?有至少兩種方法可以做到這一點︰
創建 iframe,然後導航到: data:text/html URL (在 Firefox 中不起作用,因為FF會把data: URL 作為同源來看待)。
創建帶有沙盒屬性但是沒有allow-same-origin的iframe (在 Chrome 和 Firefox 都有效)。
所以,總結一下,我們已經可以創建父頁面不可訪問的HTML節點。唯一的問題是,如何使它易於向後兼容。所以,問題又來了:
CSS不會傳遞到iframe,是為了解決這個問題,我們可以將計算的樣式傳遞到iframe的body中,注意,這將使得我們確保iframe中的文本看起來和在父頁面中一樣,但是元素選擇器在iframe裡面不起作用)。
事件不會傳遞到父頁面,為了解決這個問題,我們可以向iframe中插入一個簡單的腳本,將所有事件從iframe轉發到父頁面的document中。
這樣的做法非常類似於隱藏的HTML,而且也沒有明顯的將信息泄漏給惡意的腳本的結果。
要在demo中實現這個思路的一個簡單的方法是在innerHTML上創建一個setter函數,每當被隔離的腳本嘗試設置innerHTML的值時,我們就改為創建一個帶有沙盒屬性的iframe並將內容寫到這個iframe中,我們可以使用postMessage 將CSS和HTML傳遞到iframe。並創建一個可用於將事件傳遞到iframe的消息通道。為了避免混入其他代碼依賴,我們可以在封閉的Shadow DOM中創建此iframe。
Malte提出的這個特性的一個潛在問題是,根據實現,這可能會將開發者的體驗搞亂,因為一些腳本很可能認為DOM中的一些元素是可以正常訪問到的(例如,通過querySelector,getElementsByTagName或其他代碼)。考慮這點非常重要,也可能是最寶貴的教訓,像我這樣的安全人員在設計一些比較奇怪的限制性的API時,我們也應該問問web開發人員想要在頁面做什麼事情。
Demo
好了!現在我解釋了「腳本隔離」的概念,以及它如何在demo中實現,現在是時候讓你在行動中看到它了。
首先,要模擬瀏覽器的行為,你需要安裝一個chrome擴展(不要擔心,沒有可怕的許可權),然後去訪問「存在漏洞的網站」,並試圖竊取XHR響應的內容!如果你可以竊取成功,那麼你將會贏得一個特別的 「Isolated Scripts」 ??T恤 :D。
所以,讓我們來做吧!
1. 安裝Chrome擴展程序
2. 訪問腳本隔離PoC網站
這個網站中有XSS的頁面無處不在(DOM和反射),我禁用了XSS過濾器,使你的感到更輕鬆一些。我對於了解突破這個防禦思路的不同方法超級有興趣!
請注意,實現中可能導致的Bug在實際的瀏覽器實現上不會有問題的,但無論如何請讓我知道這些bug,儘管它們不能完全否定這個設計思路,我會修復掉這些bug,使這個設計思路儘可能的接近現實。
如果你需要它,Chrome擴展的源代碼在這裡,需要更改禁用CSP和啟用腳本隔離的代碼在這裡。
分析
現在,我明顯有點偏見,因為我已經在這個想法中投入了一些時間,而且我認為這至少有一些希望和潛力。也就是說,我想對它的價值和影響做一個分析,以避免期望過高。我將使用框架來衡量我在上一篇博文中描述到的Web安全緩解措施(如果你還沒有閱讀它,不用擔心,我將在下面做一些解釋)。
合理性
合理性標準:我們在多大程度上限制了問題的影響結果?
在這種情況下,影響結果非常容易衡量(模塊在實現上有缺陷)。
使用隔離的Cookie保護的任何私密數據僅暴露給被隔離的腳本。而被隔離的腳本所能接觸到的任何數據對於XSS來說都是隱藏的。因此,web開發人員可以決定他所需要的限制程度。
Mario提出了一個有趣的警告,在這種保護措施下,仍然有可能使用XSS進行網路釣魚攻擊,並且很可能會導致一小部分社會工程學攻擊成功。
最小化
最小化標準:我們最小化問題的數量有多少?
我們也可以計算這個問題。大多數XSS漏洞利用,包括內容嗅探和基於插件的SOP繞過(甚至一些通用類型的XSS錯誤!)都可以使用腳本隔離進行緩解防禦,但對一些DOM型的 XSS不起作用。
之所以無法通過「腳本隔離」緩解DOM XSS,如Angular模板注入和基於eval()的XSS —— 是因為它們仍然繼承了被隔離的腳本的一些能力,這類XSS漏洞的觸發點本身就包含在被隔離的腳本中也就是我們受信任的腳本中。
我很想知道還有哪些類型的bug,無法利用腳本隔離這種方法緩解。
替代
替代標準:我們用更安全的替代方法能解決的風險有多少?
我們還可以快速衡量我們用腳本隔離解決當前風險的程度。特別是,這個方法的實際採用操作似乎很簡單,儘管它還有一些問題:
1. 託管在CDN中的JavaScript代碼不能在 Isolated World中運行。這個是預期中的設計思路,不過可能會限制部署的便利性。解決這個問題的一個簡單的方法是使用ES6 import語句。
2. 希望能夠訪問「Secret HTML」(例如廣告)的代碼將不能正常執行,並且可能會以意想不到的方式運行失敗(注意,在瀏覽器實現中,實際上給予Secret HTML訪問Isolated腳本的許可權可能是有意義的,如果可能的話)。
我非常感興趣聽到任何其他問題,包括你認為web開發人員在採用這種方法時的一些想法。
簡化
簡化標準:我們要解決多少問題,而不是增加複雜性?
一般來說,我們在增加了複雜性的同時也消除了複雜性,決定它們是否能夠彼此抵消的想法看起來有點主觀。
一方面,我們讓所有與Secret HTML的內容進行交互都通過單個渠道進行,從而消除了複雜性。
但另一方面,通過添加了另一個cookie標誌和用於腳本執行的新條件,以及新的DOM隔離類型,我們這樣做使得web平台更難以被理解。
老實說,我們這樣做確實有點複雜了。不過在沒有其他緩解措施的情況下,這只是稍微有點複雜,畢竟沒有其他的防禦辦法了。我非常有興趣聽到任何關於這個思路應用於web平台的複雜性的任何爭論。
本文參考來源於sirdarckcat,如若轉載,請註明來源於嘶吼: 使用腳本隔離技術對抗XSS攻擊 更多內容請關注「嘶吼專業版」——Pro4hou
推薦閱讀:
※記一次沒什麼技術含量的XSS注入
※Content Security Policy (CSP) 是什麼?為什麼它能抵禦 XSS 攻擊?
※XSS introduction
※好書一起讀(147):XSS,CSRF,SQL注入
※XSS學習之以關點面 11~15關