內容安全策略(CSP)_防護_XSS_攻擊的好助手

原文連接php

摘要: 什麼是 CSP? 其核心思想十分簡單:網站經過發送一個 CSP 頭部,來告訴瀏覽器什麼是被受權執行的與什麼是須要被禁止的。 這裏有一個 PHP 的例子: <?php header("Content-Security-Policy: <your directives>"); ?> 一些指令 你能夠定義一些全局規則或者定義一些涉及某一類資源的規則: default-src 'self' ; # self = 同端口,同域名,同協議 => 容許 基礎參數是 default-src:若是沒有爲某一類資源設置指令規則,那麼瀏覽器就會使用這個默認參數值。css

什麼是 CSP?

其核心思想十分簡單:網站經過發送一個 CSP 頭部,來告訴瀏覽器什麼是被受權執行的與什麼是須要被禁止的。前端

這裏有一個 PHP 的例子:web

 
 
  1. <?php
  2. header("Content-Security-Policy: <your directives>");
  3. ?>

一些指令

你能夠定義一些全局規則或者定義一些涉及某一類資源的規則:數據庫

 
 
  1. default-src 'self' ;
  2. # self = 同端口,同域名,同協議 => 容許

基礎參數是 default-src:若是沒有爲某一類資源設置指令規則,那麼瀏覽器就會使用這個默認參數值。json

 
 
  1. script-src 'self' www.google-analytics.com ;
  2. # 來自這些域名的 JS 文件 => 容許

在這個例子中,咱們已經受權了 www.google-analytics.com 這個域名來源的 JavaScript 文件使用到咱們的網站上。咱們也添加了 'self' 這個關鍵詞;若是咱們經過 script-src 來從新設置其它的規則指令,它將會覆蓋 default-src 規則。小程序

若是沒有指明協議(scheme)或端口,它就會強制選擇與當前頁面相同的協議或端口。這樣作防止了混合內容(LCTT 譯註:混合內容指 HTTPS 頁面中也有非 HTTPS 資源,可參見: https://developer.mozilla.org/zh-CN/docs/Security/MixedContent )。若是頁面是 https://example.com,那麼你將沒法加載 http://www.google-analytics.com/file.js 由於它已經被禁止了(協議不匹配)。然而,有一個例外就是協議的提高是被容許的。若是 http://example.com 嘗試加載 https://www.google-analytics.com/file.js,接着協議或端口容許被更改以便協議的提高。瀏覽器

 
 
  1. style-src 'self' data: ;
  2. # Data-Uri 嵌入 CSS => 容許

在這個例子中,關鍵詞 data: 受權了在 CSS 文件中 data 內嵌內容。安全

在 CSP 1 規範下,你也能夠設置以下規則:app

  • img-src 有效的圖片來源
  • connect-src 應用於 XMLHttpRequest(AJAX),WebSocket 或 EventSource
  • font-src 有效的字體來源
  • object-src 有效的插件來源(例如,<object><embed><applet>
  • media-src 有效的 <audio> 和 <video> 來源

CSP 2 規範包含了以下規則:

  • child-src 有效的 web workers 和 元素來源,如 <frame> 和 <iframe> (這個指令用來替代 CSP 1 中廢棄了的 frame-src 指令)
  • form-action 能夠做爲 HTML <form> 的 action 的有效來源
  • frame-ancestors 使用 <frame><iframe><object><embed> 或 <applet> 內嵌資源的有效來源
  • upgrade-insecure-requests 命令用戶代理來重寫 URL 協議,將 HTTP 改到 HTTPS (爲一些須要重寫大量陳舊 URL 的網站提供了方便)。

爲了更好的向後兼容一些廢棄的屬性,你能夠簡單的複製當前指令的內容同時爲那個廢棄的指令建立一個相同的副本。例如,你能夠複製 child-src 的內容同時在 frame-src 中添加一份相同的副本。

CSP 2 容許你添加路徑到白名單中(CSP 1 只容許域名被添加到白名單中)。所以,相較於將整個 www.foo.com 域添加到白名單,你能夠經過添加 www.foo.com/some/folder 這樣的路徑到白名單中來做更多的限制。這個須要瀏覽器中 CSP 2 的支持,但它很明顯更安全。

一個例子

我爲 Web 2015 巴黎大會上個人演講 「CSP in Action」製做了一個簡單的例子。

在沒有 CSP 的狀況下,頁面展現以下圖所示:

不是十分優美。要是咱們啓用了以下的 CSP 指令又會怎樣呢?

 
 
  1. <?php
  2. header("Content-Security-Policy:
  3. default-src 'self' ;
  4. script-src 'self' www.google-analytics.com stats.g.doubleclick.net ;
  5. style-src 'self' data: ;
  6. img-src 'self' www.google-analytics.com stats.g.doubleclick.net data: ;
  7. frame-src 'self' ;");
  8. ?>

瀏覽器將會做什麼呢?它會(很是嚴格的)在 CSP 基礎規則之下應用這些指令,這意味着任何沒有在 CSP 指令中被受權容許的都將會被禁止(「blocked」 指的是不被執行、不被顯示而且不被使用在網站中)。

在 CSP 的默認設置中,內聯腳本和樣式是不被受權的,意味着每個 <script>onclick 事件屬性或style 屬性都將會被禁止。你可使用 style-src 'unsafe-inline' ; 指令來受權使用內聯 CSS。

在一個支持 CSP 的現代瀏覽器中,上述示例看起來以下圖:

發生了什麼?瀏覽器應用了指令而且拒絕了全部沒有被受權的內容。它在瀏覽器調試終端中發送了這些通知:

若是你依然不肯定 CSP 的價值,請看一下 Aaron Gustafson 文章 「More Proof We Don't Control Our Web Pages」。

固然,你可使用比咱們在示例中提供的更嚴格的指令:

  • 設置 default-src 爲 'none'
  • 爲每條規則指定你的設置
  • 爲請求的文件指定它的絕對路徑

更多關於 CSP 的信息

支持

CSP 不是一個須要複雜的配置才能正常工做的每日構建特性。CSP 1 和 2 是候選推薦標準!瀏覽器能夠很是完美的支持 CSP 1

CSP 2 是較新的規範,所以對它的支持會少那麼一點。

如今 CSP 3 仍是一個早期草案,所以尚未被支持,可是你依然可使用 CSP 1 和 2 來作一些重大的事。

其餘須要考慮的因素

CSP 被設計用來下降跨站腳本攻擊(XSS)的風險,這就是不建議開啓內聯腳本和 script-src 指令的緣由。Firefox 對這個問題作了很好的說明:在瀏覽器中,敲擊 Shift + F2 而且鍵入 security csp,它就會向你展現指令和對應的建議。這裏有一個在 Twitter 網站中應用的例子:

若是你確實須要使用內聯腳本和樣式的話,另外一種可能就是生成一份散列值。例如,咱們假定你須要使用以下的內聯腳本:

 
 
  1. <script>alert('Hello, world.');</script>

你應該在 script-src 指令中添加 sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng= 做爲有效來源。這個散列值用下面的 PHP 腳本執行得到的結果:

 
 
  1. <?php
  2. echo base64_encode(hash('sha256', "alert('Hello, world.');", true));
  3. ?>

我在前文中說過 CSP 被設計用來下降 XSS 風險,我還得加上「……與下降未經請求內容的風險。」伴隨着 CSP 的使用,你必須知道你內容的來源是哪裏與它們在你的前端都做了些什麼(內聯樣式,等)。CSP 同時能夠幫助你讓貢獻者、開發人員和其餘人員來遵循你內容來源的規則!

如今你的問題就只是,「不錯,這很好,可是咱們如何在生產環境中使用它呢?」

如何在現實世界中使用它

想要在第一次使用 CSP 以後就失望透頂的方法就是在生產環境中測試。不要想固然的認爲,「這會很簡單。個人代碼是完美而且至關清晰的。」不要這樣做。我這樣幹過。相信我,這至關的蠢。

正如我以前說明的,CSP 指令由 CSP 頭部來激活,這中間沒有過渡階段。你偏偏就是其中的薄弱環節。你可能會忘記受權某些東西或者遺忘了你網站中的一小段代碼。CSP 不會饒恕你的疏忽。然而,CSP 的兩個特性將這個問題變得至關的簡單。

report-uri

還記得 CSP 發送到終端中的那些通知麼?report-uri 指令能夠被用來告訴瀏覽器發送那些通知到指定的地址。報告以 JSON 格式送出。

 
 
  1. report-uri /csp-parser.php ;

所以,咱們能夠在 csp-parser.php 文件中處理有瀏覽器送出的數據。這裏有一個由 PHP 實現的最基礎的例子:

 
 
  1. $data = file_get_contents('php://input');
  2. if ($data = json_decode($data, true)) {
  3. $data = json_encode(
  4. $data,
  5. JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES
  6. );
  7. mail(EMAIL, SUBJECT, $data);
  8. }

這個通知將會被轉換成爲一封郵件。在開發過程當中,你可能不會須要比這更復雜的其它東西。

對於一個生產環境(或者是一個有較多訪問的開發環境),你應該使用一種比郵件更好的收集信息的方式,由於這種方式在節點上沒有驗證和速率限制,而且 CSP 可能變得亂哄哄的。只需想像一個會產生 100 個 CSP 通知(例如,一個從未受權來源展現圖片的腳本)而且天天會被瀏覽 100 次的頁面,那你就會天天收到 10000 個通知啊!

例如 report-uri.io 這樣的服務能夠用來簡化你的通知管理。你也能夠在 GitHub上看一些另外的使用report-uri (與數據庫搭配,添加一些優化,等)的簡單例子。

report-only

正如咱們所見的,最大的問題就是在使用和不使用 CSP 之間沒有中間地帶。然而,一個名爲 report-only 的特性會發送一個稍有不一樣的頭部:

 
 
  1. <?php
  2. header("Content-Security-Policy-Report-Only: <your directives>");
  3. ?>

總的來講,這個頭部就是告訴瀏覽器,「表現得彷佛全部的 CSP 指令都被應用了,可是不由止任何東西。只是發送通知給本身。」這是一種至關棒的測試指令的方式,避免了任何有價值的東西被禁止的風險。

在 report-only 和 report-uri 的幫助下你能夠毫無風險的測試 CSP 指令,而且能夠實時的監控網站上一切與 CSP 相關的內容。這兩個特性對部署和維護 CSP 來講真是至關的有用!

結論

爲何 CSP 很酷

CSP 對你的用戶來講是尤爲重要的:他們在你的網站上再也不須要遭受任何的未經請求的腳本,內容或 XSS 的威脅了。

對於網站維護者來講 CSP 最重要的優點就是可感知。若是你對圖片來源設置了嚴格的規則,這時一個腳本小子嘗試在你的網站上插入一張未受權來源的圖片,那麼這張圖片就會被禁止,而且你會在第一時間收到提醒。

開發者也須要確切的知道他們的前端代碼都在作些什麼,CSP 能夠幫助他們掌控一切。會促使他們去重構他們代碼中的某些部分(避免內聯函數和樣式,等)而且促使他們遵循最佳實踐。

如何讓 CSP 變得更酷

諷刺的是,CSP 在一些瀏覽器中過度的高效了,在和書籤欄小程序一塊兒使用時會產生一些 bug。所以,不要更新你的 CSP 指令來容許書籤欄小程序。咱們沒法單獨的責備任何一個瀏覽器;它們都有些問題:

  • Firefox
  • Chrome (Blink)
  • WebKit

大多數狀況下,這些 bug 都是禁止通知中的誤報。全部的瀏覽器提供者都在努力解決這些問題,所以咱們能夠期待很快就會被解決。不管怎樣,這都不會成爲你使用 CSP 的絆腳石。

原文連接

相關文章
相關標籤/搜索