天天看點

防禦XSS攻擊的七條原則

攻擊者可以利用xss漏洞向使用者發送攻擊腳本,而使用者的浏覽器因為沒有辦法知道這段腳本是不可信的,是以依然會執行它。對于浏覽器而言,它認為這段腳本是來自可以信任的伺服器的,是以腳本可以光明正大地通路cookie,或者儲存在浏覽器裡被目前網站所用的敏感資訊,甚至可以知道使用者電腦安裝了哪些軟體。這些腳本還可以改寫html頁面,進行釣魚攻擊。

雖然産生xss漏洞的原因各種各樣,對于漏洞的利用也是花樣百出,但是如果我們遵循本文提到防禦原則,我們依然可以做到防止xss攻擊的發生。

有人可能會問,防禦xss的核心不就是在輸出不可信資料的時候進行編碼,而現如今流行的web架構(比如rails)大多都在預設情況下就對不可信資料進行了html編碼,幫我們做了防禦,還用得着我們自己再花時間研究如何防禦xss嗎?答案是肯定的,對于将要放置到html頁面body裡的不可信資料,進行html編碼已經足夠防禦xss攻擊了,甚至将html編碼後的資料放到html标簽(tag)的屬性(attribute)裡也不會産生xss漏洞(但前提是這些屬性都正确使用了引号),但是,如果你将html編碼後的資料放到了 直接插入到script标簽裡 <!– …不要在這裡直接插入不可信資料… –> 插入到html注釋裡

插入到html标簽的屬性名裡

插入到html标簽的屬性值裡 <不要在這裡直接插入不可信資料 href=”…”> 作為html标簽的名字直接插入到css裡

最重要的是,千萬不要引入任何不可信的第三方javascript到頁面裡,一旦引入了,這些腳本就能夠操縱你的html頁面,竊取敏感資訊或者發起釣魚攻擊等等。

<a target="_blank"></a>

在這裡相當強調是往html标簽之間插入不可信資料,以差別于往html标簽屬性部分插入不可信資料,因為這兩者需要進行不同類型的編碼。當你确實需要往html标簽之間插入不可信資料的時候,首先要做的就是對不可信資料進行html entity編碼。比如,我們經常需要往div,p,td這些标簽裡放入一些使用者送出的資料,這些資料是不可信的,需要對它們進行html entity編碼。很多web架構都提供了html entity編碼的函數,我們隻需要調用這些函數就好,而有些web架構似乎更“智能”,比如rails,它能在預設情況下對所有插入到html頁面的資料進行html entity編碼,盡管不能完全防禦xss,但着實減輕了開發人員的負擔。

…插入不可信資料前,對其進行html entity編碼…

[編碼規則]

那麼html entity編碼具體應該做哪些事情呢?它需要對下面這6個特殊字元進行編碼:

有兩點需要特别說明的是:

不推薦将單引号( ‘ )編碼為 ' 因為它并不是标準的html标簽

需要對斜杠号( / )編碼,因為在進行xss攻擊時,斜杠号對于關閉目前html标簽非常有用

這條原則是指,當你要往html屬性(例如width、name、value屬性)的值部分(data value)插入不可信資料的時候,應該對資料進行html屬性編碼。不過需要注意的是,當要往html标簽的事件處理屬性(例如onmouseover)裡插入資料的時候,本條原則不适用,應該用下面介紹的原則4對其進行javascript編碼。

除了阿拉伯數字和字母,對其他所有的字元進行編碼,隻要該字元的ascii碼小于256。編碼後輸出的格式為 (以&amp;#x開頭,hh則是指該字元對應的十六進制數字,分号作為結束符)

之是以編碼規則如此嚴格,是因為開發者有時會忘記給屬性的值部分加上引号。如果屬性值部分沒有使用引号的話,攻擊者很容易就能閉合掉目前屬性,随後即可插入攻擊腳本。例如,如果屬性沒有使用引号,又沒有對資料進行嚴格編碼,那麼一個空格符就可以閉合掉目前屬性。請看下面這個攻擊:

假設html代碼是這樣的:

…content…

攻擊者可以構造這樣的輸入:

最後,在使用者的浏覽器裡的最終html代碼會變成這個樣子:

隻要使用者的滑鼠移動到這個div上,就會觸發攻擊者寫好的攻擊腳本。在這個例子裡,腳本僅僅彈出一個警告框,除了惡作劇一下也沒有太多的危害,但是在真實的攻擊中,攻擊者會使用更加具有破壞力的腳本,例如下面這個竊取使用者cookie的xss攻擊:

除了空格符可以閉合目前屬性外,這些符号也可以:

可以使用esapi提供的函數進行html屬性編碼:

這條原則主要針對動态生成的javascript代碼,這包括腳本部分以及html标簽的事件處理屬性(event handler,如onmouseover, onload等)。在往javascript代碼裡插入資料的時候,隻有一種情況是安全的,那就是對不可信資料進行javascript編碼,并且隻把這些資料放到使用引号包圍起來的值部分(data value)之中,例如:

除此之外,往javascript代碼裡其他任何地方插入不可信資料都是相當危險的,攻擊者可以很容易地插入攻擊代碼。

值部分使用了引号,且事件處理屬性的值部分也使用了引号 特别需要注意的是,在xss防禦中,有些javascript函數是極度危險的,就算對不可信資料進行javascript編碼, 也依然會産生xss漏洞,例如:

除了阿拉伯數字和字母,對其他所有的字元進行編碼,隻要該字元的ascii碼小于256。編碼後輸出的格式為 \xhh (以 \x 開頭,hh則是指該字元對應的十六進制數字)

在對不可信資料做編碼的時候,千萬不能圖友善使用反斜杠( \ )對特殊字元進行簡單轉義,比如将雙引号 ” 轉義成 \” ,這樣做是不可靠的,因為浏覽器在對頁面做解析的時候,會先進行html解析,然後才是javascript解析,是以雙引号很可能會被當做html字元進行html解析,這時雙引号就可以突破代碼的值部分,使得攻擊者可以繼續進行xss攻擊。例如:

假設代碼片段如下:

攻擊者輸入的内容為:

如果隻是對雙引号進行簡單轉義,将其替換成 \” 的話,攻擊者輸入的内容在最終的頁面上會變成:

浏覽器在解析的時候,會認為反斜杠後面的那個雙引号和第一個雙引号相比對,繼而認為後續的alert(‘xss’)是正常的javascript腳本,是以允許執行。

可以使用esapi提供的函數進行javascript編碼:

當需要往stylesheet,style标簽或者style屬性裡插入不可信資料的時候,需要對這些資料進行css編碼。傳統印象裡css不過是負責頁面樣式的,但是實際上它比我們想象的要強大許多,而且還可以用來進行各種攻擊。是以,不要對css裡存放不可信資料掉以輕心,應該隻允許把不可信資料放入到css屬性的值部分,并進行适當的編碼。除此以外,最好不要把不可信資料放到一些複雜屬性裡,比如url, behavior等,隻能被ie認識的expression屬性允許執行javascript腳本,是以也不推薦把不可信資料放到這裡。

除了阿拉伯數字和字母,對其他所有的字元進行編碼,隻要該字元的ascii碼小于256。編碼後輸出的格式為 \hh (以 \ 開頭,hh則是指該字元對應的十六進制數字)

同原則2,原則3,在對不可信資料進行編碼的時候,切忌投機取巧對雙引号等特殊字元進行簡單轉義,攻擊者可以想辦法繞開這類限制。

可以使用esapi提供的函數進行css編碼:

當需要往html頁面中的url裡插入不可信資料的時候,需要對其進行url編碼,如下:

除了阿拉伯數字和字母,對其他所有的字元進行編碼,隻要該字元的ascii碼小于256。編碼後輸出的格式為 %hh (以 % 開頭,hh則是指該字元對應的十六進制數字)

在對url進行編碼的時候,有兩點是需要特别注意的:

1) url屬性應該使用引号将值部分包圍起來,否則攻擊者可以很容易突破目前屬性區域,插入後續攻擊代碼

2) 不要對整個url進行編碼,因為不可信資料可能會被插入到href, src或者其他以url為基礎的屬性裡,這時需要對資料的起始部分的協定字段進行驗證,否則攻擊者可以改變url的協定,例如從http協定改為data僞協定,或者javascript僞協定。

可以使用esapi提供的函數進行url編碼:

esapi還提供了一些用于檢測不可信資料的函數,在這裡我們可以使用其來檢測不可信資料是否真的是一個url:

web應用一般都會提供使用者輸入富文本資訊的功能,比如bbs發帖,寫部落格文章等,使用者送出的富文本資訊裡往往包含了html标簽,甚至是javascript腳本,如果不對其進行适當的編碼過濾的話,則會形成xss漏洞。但我們又不能因為害怕産生xss漏洞,是以就不允許使用者輸入富文本,這樣對使用者體驗傷害很大。

<b> 原文釋出時間為:2013-05-29</b>

<b>本文來自雲栖社群合作夥伴“linux中國”</b>

繼續閱讀