標籤:

DOM-based XSS 與存儲性 XSS、反射型 XSS 有什麼區別?


單單解釋三者的區別,似乎有點單調。說到xss,就必須要提一提js,脫離了js去談xss都是耍流氓!

1,)先來分析一下LZ說的DOM-based XSS。

一句話概括:DOM—based XSS漏洞是基於文檔對象模型Document Objeet Model,DOM)的一種漏洞。

如果樓主沒有搞懂dom樹的關係,那對dom xss的了解是非常有限的。

先以一張w3c的圖來說明,到底什麼是dom:

哦..原來是這樣(似乎你有點了解?),沒錯,dom就是一個樹狀的模型,你可以編寫Javascript代碼根據dom一層一層的節點,去遍歷/獲取/修改對應的節點,對象,值。

了解了這麼一個知識點,你就會發現,其實dom xss並不複雜,他也屬於反射型xss的一種(2016.3.16修改,domxss取決於輸出位置,並不取決於輸出環境,因此domxss既有可能是反射型的,也有可能是存儲型的),簡單去理解就是因為他輸出點在DOM,所以在道哥的《白帽子講Web安全里》也有詳細介紹。dom - xss是通過url傳入參數去控制觸發的。

2,)分析完dom-xss之後,再說說存儲型xss,其實也很好理解,存儲型xss,自然就是存入了資料庫,再取出來,導致的xss。

3,)反射型xss實際上是包括了dom - xss了,關鍵點仍然是在通過url控制了頁面的輸出(dom-xss也類似,只因為輸出地點不同而導致結果不一致)。

說了這3種區別,不能僅僅停留在理論上,下面我貼出這三種xss代碼的demo【均以php為例】:

dom-xss:

&
&
&& &

步驟一,是為了讓xss代碼寫入資料庫,步驟二是為了把惡意代碼從資料庫取出來並且輸出在頁面上。

反射型xss:

&
&

結果:

好了,花了這麼長的時間去說明,其實想告訴題主:

在易用上,存儲型XSS &> DOM - XSS &> 反射型 XSS。

為什麼這麼說?因為存儲型xss最持久,而且更為隱蔽,因為是存在資料庫當中的,觸發的url當中沒有帶js或者其他的html代碼。dom-xss,排在其次。為何?上面的圖最直觀:因為它能繞過大部分瀏覽器的過濾(新版Chrome針對script context類型的domxss做了檢測攔截)。 反射型的xss還要深思熟慮的考慮根據瀏覽器去bypass各種過濾,易用性稍微差一些。註:反射型xss和dom-xss都需要在url加入js代碼才能夠觸發。

在檢測上,目前我還沒有看到比較好的針對存儲型xss的檢測手段,而domxss和反射型xss均有比較好的檢測手段了。

其實這個問題早已有人問過了:存儲型XSS與反射型XSS有什麼區別?黑哥的總結是交互,這點我也是贊同的,這個是相同點。

希望我這個答案是升級版,能幫助到新手學習xss,這樣我碼字這麼多也是值得的:)

資料參考:

HTML DOM 教程

學習教程:

白帽子信息_心傷的瘦子

2014-11-17修改:

在新版本的Chrome已經能夠攔截scrip context類型的dom-xss,感謝 @ice png指正錯誤,我在Chrome的老版本親測是可行的:)版本號10.0.648.6


XSS就是XSS。所謂「存儲型」、「反射型」都是從黑客利用的角度區分的。對於程序員來說意義不大,反而是誤導。只有「DOM-based」型略有不同。

XSS、SQL injection之類的漏洞,原理都是破壞跨層協議的數據/指令的構造。

如SQL注入,涉及應用層和資料庫層。協議是SQL查詢語言。對於應用層來說,一句sql是數據(字元串);對於資料庫層來說,一句sql是指令。sql注入的原理,就是破壞sql的構造。防禦的方法,就是用參數查詢(現代所有的資料庫驅動的api一定包含了)而不是自己拼sql字元串。這個已經是所有後端工程師的基本常識了。

而XSS,兩個層次是伺服器端和瀏覽器端。協議就是HTML/CSS/JavaScript。對於伺服器端來說,html是數據(字元串);對於瀏覽器端來說,html是指令。XSS的原理,就是破壞html/css/js的構造。

防禦的方法,一般認為是正確escape,就是替換尖括弧、引號等特殊符號。

但是這是不夠的,因為這隻解決了html的問題。考慮如下:

&var name = "&";&

這代碼顯然有XSS隱患。

那麼我們escape一下,是不是就好了?

&var name = "&";&

很遺憾這樣是沒用的。因為這裡是javascript輸出點,xss破壞的目標是破壞js構造而不是html構造。html構造中的關鍵字元是尖括弧、雙引號、「」符號等。而js構造就複雜了,比如換行、注釋(//和/*)、引號(包括單引號)等都會改變構造。

為了確保js構造的正確,應該:

&var name = &;&

不過這還是存在一個漏洞。(作為一個簡單的習題留給同志們。)

【另外PHP不同版本的json_encode的行為不一致,幾乎都有問題,雖然不至於直接XSS,但存在被利用的可能。】

顯然,PHP沒有提供比較便利的方式來確保代碼的安全,這是那個年代(199x)伺服器端腳本技術的通病。遺憾的是,web技術發展到今天,即使是常見的現代Web模板,大多提供了默認的html escape,但對inline script中的XSS防禦就乏善可陳了。

我開發的 jedi 模板(baixing/jedi · GitHub )在這點上做了較好的創新:

1. 不允許在 script 元素中直接進行內容插值。

2. 如果要給 inline script 傳遞數據,寫成:

script = $name
!
console.log(data)

會被編譯為:

&
void function(data){
console.log(data)
}(&)
&

可以看到,我用的這種方式,和sql的參數查詢是類似的,就是提供更好的抽象——API/模板指令。

回到題主的問題。所謂「存儲型」指的是$name的值來自於持久存儲,比如資料庫。「反射型」指的是$name的值來自於直接的用戶輸入(通常通過URL參數傳遞)。

黑客更喜歡「存儲型」,或者說「存儲型」的「危害」更大的原因是,$name可能會被用在一個頁面的很多地方,或者很多個頁面中,甚至多個站點中。所有這些地方很可能或者必然不是由單個程序員寫的,可能是幾個月之前,或者幾年之後寫的。黑客會掃描所有出現的地方,只要其中有一處沒有正確輸出,就是一個XSS漏洞點。大多數碼農(以及他們的manager)在面對漏洞的時候,只是頭痛醫頭腳痛醫腳。而「存儲型」因為有太多可能的點,所以常給人以防不勝防,堵不勝堵的感覺。

對於「存儲型」,有一種看似可行的防禦方法是,在存入資料庫時預先escape,這樣只要在存入這個單點保證代碼正確,而不用管在哪裡輸出。看上去是一勞永逸,但是實際上是糟糕的應對方法。

為什麼?

翻看答案前請開動腦筋想一下。

提示:前面的例子已經包含了原因。

答案:此方式實際上限制了所有輸出點都是html,但絕大多數情況下,你無法也不應預設所有輸出的點必須是html。常見的情況是,有些地方需要html輸出,有些地方需要js輸出,有些地方可能是純文本輸出(比如手機客戶端)。

html輸出和js輸出需要做的escape是不同的,如果是純文本輸出,你根本不需要escape。如果你同時做多種escape,你就要正確的unescape,顯然就喪失了不用管輸出點的「優點」。

最後說一下 DOM-based XSS。直接來一個反面教材吧。

百度翻譯不久前爆了一個xss漏洞。百度翻譯除了給出翻譯結果之外,如果這個詞有百度百科詞條的話,也會通過ajax拉百度百科的內容並顯示在頁面上。問題他們是直接把得到的百度百科內容通過innerHTML插入頁面,並且沒有做escape,這導致百度百科的內容中的html標籤(比如那些html、css、js相關的詞條上的代碼sample)直接就生效了。可想而知,你只要修改一下百度百科上的詞條就能輕鬆讓對應的百度翻譯頁面帶有XSS漏洞……

「DOM-based」確實有一點獨特性。那就是涉及的層次不同。所謂基於DOM的XSS,涉及的兩個層次不是伺服器端和瀏覽器端,而是瀏覽器端的JavaScript層和HTML層。更準確的說,就是伺服器腳本變成了客戶端腳本。

因為改寫innerHTML在傳統前端開發中非常常見,這情形就好像當年伺服器端腳本直接拼html一樣(我的第一個php的例子)。另一方面,如果存在eval或者new Function調用,前述的針對js構造的XSS攻擊也是可能的。

那麼對於所謂 DOM-based XSS 的正確的防禦辦法是什麼呢?

比照一下就知道了,一個就是一樣的正確escape。另一個自然就是用更好的抽象——在這裡就是DOM API了。比如通過document.createTextNode(someText)創建文本節點後插入,自然不可能有XSS的可能性。不過之所以我們都用innerHTML,不就是因為DOM API用起來不順手嗎?(慢倒還在其次,因為現代瀏覽器的腳本引擎都優化了,以至於不再慢甚至反超innerHTML了。)這個時候新的Template元素就是大殺器了——詳細這裡不再展開,請自行學習體會。當然如果你要考慮兼容性,那麼用一款靠譜的前端模板也是可行的方案。

轉回來,如果你去看百度翻譯的修補方法,並沒有採取上面的方式(如在寫入innerHTML時做escape),而是,在ajax輸出時,確保欄位內容是escape過的。這跟前面說的「存儲型」漏洞的那種看似可行的方案類似。當然,這個ajax調用作為百度翻譯的一個內部介面來說,是可以內部約定某個欄位的格式本身是html——這比限制資料庫欄位必須是html要好多了——畢竟如果有需求的話你可以再開個介面返迴文本而不是html。但是從大原則來說,這仍然是一個糟糕的選擇。比如,如果這是一個外部(第三方)介面——假設百度翻譯直接通過CORS XHR讀取了360百科的內容——就是不可接受的。一來,360百科未必給你想要的格式,二來,也是更重要的,你的安全是基於別人的介面返回是否正確(或者不被黑)的基礎上,你說行不行?

好了,扯了那麼多,最重要的一點就是,不要光知道幾個名詞的差別。作為程序員,更重要的是知道原理,看清本質,才能不被名詞所迷惑。

【更新:關於PHP中 json_encode 存在的問題,請直接看我寫的 safe_json_encode —— https://gist.github.com/hax/75706bac6d89dfeb7195 。注意gist最近被DNS污染了,請自行使用DNSCrypt或直接翻牆訪問,並預祝病魔早日戰勝方前校長100次。】


DOM XSS 是由於瀏覽器解析機制導致的漏洞,伺服器不參與,而存儲型與反射型都需要伺服器響應參與


我覺得有兩種分類方式。第一,dom型和非dom型。第二,反射型和存儲型。這是兩種不同的分類。


你用burp Suite或者其他WEB數據包分析工具分析一下就會發現:

存儲型XSS:你發送一次帶XSS代碼的請求,以後這個頁面的返回包里都會有XSS代碼;

反射型XSS:你發送一次帶XSS代碼的請求,只能在當前返回的數據包中發現XSS代碼;

DOM型XSS:你發送一次帶XSS代碼的請求,在返回包里壓根兒就找不到XSS代碼的影子;


@梧桐雨

你好,很詳細的回答,但是對DOM XSS中「因為它能繞過瀏覽器的過濾」這不是很理解,如果說繞過伺服器端的檢測還是可以理解的。。。

比如:

在chrome下:

謝謝,(因為評論中好像不能發圖。。)


%c0u003cimg+src%3d1+onerror%3dalert(/xss/)+%c0u003e


推薦閱讀:

XSS 攻擊時怎麼繞過 htmlspecialchars 函數呢?
XSS 攻擊有哪些黑魔法?
如何評價 XSS 在黑客攻防中的地位或重要性?
Content Security Policy (CSP) 是什麼?為什麼它能抵禦 XSS 攻擊?

TAG:XSS |