內容安全政策

Mike West
Joe Medley
Joe Medley

網路的安全模型是源自於 同源政策。編程 的 https://mybank.com 只能存取 https://mybank.com 的 資料和「https://evil.example.com」當然是一律禁止存取的。 每個來源都會與網路的其他部分隔開,確保開發人員安全無虞 並在其中建構及暢玩的沙箱。理論上來說,這超棒的於 因此,攻擊者找到了聰明的方法,能夠破壞系統。

跨網站指令碼攻擊 (XSS) 例如藉由誘騙網站 傳送惡意程式碼與預期的內容這對 Android Vitals 而言相當驚人 因為瀏覽器會將網頁上顯示的所有程式碼 屬於該網頁的安全性來源。 XSS 一覽表 是攻擊者可能使用方法的舊資料交叉段 違反這項信任。如果攻擊者成功 插入任何程式碼,往往很難達成,那就是使用者工作階段資料 而且應該保密的資訊會洩露給 The Bad Road 各位,我們顯然希望能避免這種情況。

本總覽說明瞭可以大幅降低風險並降低風險的防禦機制 XSS 攻擊對現代瀏覽器的影響:內容安全政策 (CSP)。

TL;DR

  • 使用許可清單告訴用戶端允許及禁止販售的項目。
  • 瞭解可用的指令。
  • 瞭解它們採用的關鍵字。
  • 內嵌程式碼和 eval() 都視為有害。
  • 請先向伺服器回報違反政策的情形,然後再強制執行。

來源許可清單

XSS 攻擊所利用的問題在於瀏覽器無法區分 納入應用程式的指令碼和指令碼中的指令碼 遭第三方惡意植入。舉例來說,搜尋位於 從此頁面底部載入並執行 https://apis.google.com/js/plusone.js。三 ,但我們認為瀏覽器無法自行判斷該程式碼是否 apis.google.com 提供的程式碼很棒,apis.evil.example.com 但事實上可能不是瀏覽器開心地下載並執行網頁中的任何程式碼 要求 (無論來源為何)。

CSP 不會盲目信任伺服器提供的「所有項目」,而是會定義 Content-Security-Policy HTTP 標頭,可讓您建立 ,並指示瀏覽器只執行或顯示 從這些來源存取資源即使攻擊者能夠透過 插入指令碼時,該指令碼不會與許可清單相符,因此不會 執行狀態

我們相信 apis.google.com 能傳遞有效的程式碼,而且我們相信自己 接著我們再定義一項政策,僅允許指令碼在指令碼執行時執行 來自這兩個來源之一:

Content-Security-Policy: script-src 'self' https://apis.google.com

很簡單吧?您或許已經猜到,script-src 這個指令會 控制特定網頁的一組指令碼相關權限。我們已指定 將 'self' 做為有效的指令碼來源,https://apis.google.com 則當做 另一個例子。瀏覽器會從 apis.google.com 和目前網頁來源的通訊協定。

控制台錯誤:拒絕載入指令碼「http://evil.example.com/evil.js」因為此內容違反下列《內容安全政策》指令:script-src 'self'https://apis.google.com

定義這項政策後,瀏覽器只會擲回錯誤,而非 從其他來源載入指令碼。當聰明的攻擊者 將程式碼插入網站後,這類錯誤會長時間顯示在錯誤訊息中 而非他們預期的結果

這類政策適用於多項資源

雖然指令碼資源是最明顯的安全性風險,但 CSP 提供了 可以更精細地控管資源的政策指令 允許載入網頁您已看過 script-src,這個概念 都應該清晰可辨

我們來快速示範其餘的資源指令下列清單 代表指令在等級 2 的狀態。已發布第 3 級規格,但卻在主要規格尚未實作

  • base-uri 可限制網頁可在 <base> 元素中顯示的網址。
  • child-src 列出 worker 和內嵌影格內容的網址。適用對象 範例:child-src https://youtube.com 會啟用以下來源的嵌入影片: YouTube 而非其他來源。
  • connect-src 會限制可以連線的來源 (透過 XHR、 WebSockets 和 EventSource)。
  • font-src 會指定可提供網路字型的來源。Google 網路 您可以透過 font-src https://themes.googleusercontent.com 啟用字型。
  • form-action 列出可從 <form> 標記提交的有效端點。
  • frame-ancestors 會指定可嵌入目前網頁的來源。 這個指令適用於 <frame><iframe><embed><applet> 標記。 這個指令無法用於 <meta> 標記,而且只會套用至非 HTML 再複習一下,機構節點 是所有 Google Cloud Platform 資源的根節點
  • frame-src 已在第 2 級淘汰,但在第 3 級已還原。如果不是 表示現在仍可像過去一樣改回 child-src
  • img-src 定義可載入圖片的來源。
  • media-src 會限制能放送影片和音訊的來源。
  • object-src 可控制 Flash 和其他外掛程式。
  • plugin-types 會限制網頁可叫用的外掛程式類型。
  • report-uri 會指定瀏覽器在發生 違反內容安全政策。無法在 <meta> 中使用這個指令 標記內。
  • style-src是樣式表的 script-src 對應項目。
  • upgrade-insecure-requests 會指示使用者代理程式重新編寫網址配置。 將 HTTP 變更為 HTTPS這項指令適用於含有大量 需要重寫的舊網址
  • worker-src 是 CSP 級別 3 指令,用來限制可能 顯示為工作站、共用工作站或 Service Worker。截至 2017 年 7 月為止 指令包含 有限實作

指令預設為公開。假設您沒有為 指令後,假設 font-src,則該指令的預設行為為 但已指定 * 做為有效來源 (例如 不受限制)。

您可以指定 default-src 來覆寫這個預設行為 指令。這個指令會定義 未指定的指令一般而言,這適用於 結尾是 -src。如果將 default-src 設為 https://example.com,而您失敗 指定 font-src 指令,然後從中載入字型 https://example.com,不包含在其他位置。我們在script-src 也就是圖片、字型等內容 與任何來源有關

下列指令不會使用 default-src 做為備用方案。注意事項 未設定,就和允許任何項目一樣

  • base-uri
  • form-action
  • frame-ancestors
  • plugin-types
  • report-uri
  • sandbox

您可以根據自己的需求,使用多少指令來測試這些指令 只要在 HTTP 標頭中列出每個應用程式, 指令中加上分號確認已列出全部single 指令中定義特定類型的必要資源。如果您寫入 例如 script-src https://host1.com; script-src https://host2.com 就會直接忽略第二個指令如下所示 正確指定兩個來源為有效來源:

script-src https://host1.com https://host2.com

舉例來說,假設您的應用程式從某個伺服器載入所有資源 內容傳遞網路 (例如 https://cdn.example.net) 並知悉您 不含任何頁框內容或外掛程式,你的政策看起來可能會像 如下所示:

Content-Security-Policy: default-src https://cdn.example.net; child-src 'none'; object-src 'none'

實作詳情

您會看到 X-WebKit-CSPX-Content-Security-Policy 各種標題 線上教學課程日後請忽略這些前置字元 標題。新版瀏覽器 (IE 除外) 支援無前置字串的 Content-Security-Policy 標頭。這正是應使用的標頭。

無論您使用何種標頭,政策都是逐頁定義: 您必須將 HTTP 標頭連同每個想分享的回應 才能受到妥善保護這個功能提供許多彈性 根據特定需求修正特定網頁的政策也許一組 你的網站上有 +1 按鈕,有些則不允許:你可以允許 按鈕程式碼,只有在必要時才載入。

每個指令中的來源清單都是彈性的。如要指定來源 配置 (data:https:),或特定程度的範圍 (僅限主機名稱) (example.com,與該主機上的任何來源相符:任何配置、任何通訊埠) 到 完整 URI (https://example.com:443,只與 HTTPS 相符, example.com,且只有通訊埠 443)。系統接受萬用字元 (但僅限於配置) 通訊埠,或是主機名稱的最左側位置:*://*.example.com:* 會 符合 example.com 的所有子網域 (但「不是」example.com 本身),使用 和任何通訊協定都一樣

來源清單也接受四個關鍵字:

  • 正如您所預期的一樣,'none' 不會進行比對。
  • 'self' 與目前的來源相符,但與子網域的子網域不相符。
  • 'unsafe-inline' 允許內嵌 JavaScript 和 CSS,(我們將在 更多細節)。
  • 'unsafe-eval' 允許 eval 等文字轉 JavaScript 機制。(我們會 )

這些關鍵字需要單引號。例如 script-src 'self' (含引號) 授權從目前主機執行 JavaScript;script-src self (無引號) 允許從名為「self」的伺服器執行 JavaScript(而不是從 這可能不是您想要的結果

沙箱機制

還有一個指令值得討論:sandbox。有點麻煩 和我們討論的比較項目不同,因為會限制使用者執行 不會超過網頁可載入的資源如果 如果有 sandbox 指令,系統會將網頁視為已載入 在含有 sandbox 屬性的 <iframe> 內。模型可以提供多種 這會影響網頁的影響:強制網頁成為專屬來源,並阻止表單產生 以及其他可能性這有點不在本文的討論範圍內 如需有效沙箱屬性的完整資訊,請參閱 「採用沙箱機制」一節

中繼標記

CSP 的偏好傳送機制是 HTTP 標頭。這項服務很實用 直接在標記中設定網頁政策。方法是使用 <meta> 標記搭配 http-equiv 屬性:

<meta
  http-equiv="Content-Security-Policy"
  content="default-src https://cdn.example.net; child-src 'none'; object-src 'none'"
/>

無法用於 frame-ancestorsreport-urisandbox

內嵌程式碼視為有害

但應清楚指出 CSP 是以許可清單來源為基礎,因為這是 指示瀏覽器以明確的方式處理特定資源組合 並拒絕其餘的部分來源式許可清單不適用 然而,必須解決 XSS 攻擊所造成的最大威脅:內嵌指令碼插入。 如果攻擊者能夠插入直接含有部分惡意內容的指令碼標記 酬載 (<script>sendMyDataToEvilDotCom();</script>), 瀏覽器並沒有用來區分 內嵌指令碼標記。CSP 可以完全停用內嵌指令碼,藉此解決這個問題: 只有這是唯一的方式

這個停權設定不僅包含直接內嵌於 script 標記的指令碼,也包括 內嵌事件處理常式和 javascript: 網址。您需要將 將 script 標記新增至外部檔案,並將 javascript: 網址和 <a ... onclick="[JAVASCRIPT]"> 替換為適當的 addEventListener() 呼叫。例如: 您或許可以改寫以下內容:

<script>
  function doAmazingThings() {
    alert('YOU AM AMAZING!');
  }
</script>
<button onclick="doAmazingThings();">Am I amazing?</button>

例如:

<!-- amazing.html -->
<script src="amazing.js"></script>
<button id="amazing">Am I amazing?</button>

<div style="clear:both;"></div>
// amazing.js
function doAmazingThings() {
  alert('YOU AM AMAZING!');
}
document.addEventListener('DOMContentLoaded', function () {
  document.getElementById('amazing').addEventListener('click', doAmazingThings);
});

重寫的程式碼有好多優勢,無法充分發揮 CSP;無論您是否使用 CSP,都是最佳做法。內嵌 JavaScript 會以您不想要的方式混用結構和行為。 外部資源讓瀏覽器更容易快取,也更容易理解 進而編譯和壓縮你寫得更好 程式碼。

內嵌樣式的處理方式相同:style 屬性和 style 標記應合併為外部樣式表,以避免發生 有很多巧妙 而是運用 CSS 供應商 實現的資料竊取方法

如果必須使用內嵌指令碼和樣式,可以啟用 ,將 'unsafe-inline' 新增為 script-srcstyle-src 中的允許來源 指令。您也可以使用 Nonce 或雜湊 (請見下方說明)。 控管 In-Line 指令碼是 CSP 提供最大的安全性優勢。 同樣地,禁止使用內嵌樣式也會強化應用程式。編碼器-解碼器架構 事先確保所有程式碼在移動所有程式碼後能正常運作 但這才是值得做出的取捨

如果您一定要用到

CSP 級別 2 可讓您執行下列操作,為內嵌指令碼提供回溯相容性: 使用密碼編譯 Nonce (數字) 將特定內嵌指令碼加入許可清單 一次使用一次) 或雜湊雖然這可能相當麻煩,但很實用 順便一按

如要使用 Nonce,請為指令碼標記提供 Nonce 屬性。它的值必須與一個 並在信任來源清單中找到您。例如:

<script nonce="EDNnf03nceIOfn39fn3e9h3sdfa">
  // Some inline code I can't remove yet, but need to asap.
</script>

現在,請將 Nonce 附加至 nonce- 關鍵字。script-src

Content-Security-Policy: script-src 'nonce-EDNnf03nceIOfn39fn3e9h3sdfa'

請注意,每次網頁要求都必須重新產生 Nonce,且 不可猜測

「雜湊」的運作方式大同小異。您不需要將程式碼加進指令碼標記 建立指令碼的 SHA 雜湊,並新增至 script-src 指令。 舉例來說,假設您的網頁包含:

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

您的政策會包含以下內容:

Content-Security-Policy: script-src 'sha256-qznLcsROx4GACP2dm0UCKCzCG-HiZ1guq6ZZDob_Tng='

這裡有幾點要注意。sha*- 前置字串可指定演算法 來產生雜湊值在上述範例中,使用了 sha256-。CSP 也 支援 sha384-sha512-。產生雜湊時,請勿包含 <script> 標記。此外,大小寫和空白字元也包括開頭或 在結尾處的空白字元

運用產生 SHA 雜湊的 Google 搜尋,將導向所有解決方案 在 Chrome 40 以上版本中,請開啟開發人員工具,然後 重新載入網頁。「Console」分頁中會顯示錯誤訊息和正確資訊 每個內嵌指令碼的 sha256 雜湊值。

一併評估

即使攻擊者無法直接插入指令碼,他們也許可以 您的應用程式轉換為可執行的 JavaScript 並代表他們執行該工作eval(),新討論串 Function()、setTimeout([string], ...)setInterval([string], ...) 都是所有插入的向量 進而執行意想不到的惡意內容CSP 的預設 才能完全封鎖這些向量。

這對建構應用程式的方式的影響不只部分:

  • 您必須透過內建的 JSON.parse 剖析 JSON,而非依賴 eval。您可以在 才能確保安全性
  • 重新編寫目前正在進行的任何 setTimeoutsetInterval 呼叫 以內嵌函式 (而非字串) 取代例如:
setTimeout("document.querySelector('a').style.display = 'none';", 10);

這樣寫得好:

setTimeout(function () {
  document.querySelector('a').style.display = 'none';
}, 10);
  • 避免在執行階段使用內嵌範本:許多範本程式庫會盡量使用 new Function(),在執行階段加速產生範本。這是 應用動態程式設計的巧妙運用,卻有可能導致 評估惡意文字部分架構支援立即可用的 CSP, 在沒有 eval 的情況下,會改回使用完善的剖析器。 AngularJS 的 ng-csp 指令 就是個好例子

但更理想的做法是 預先編譯 (帳號代碼確實、 。預先編譯範本可大幅改善使用者體驗 執行階段導入速度更快,而且安全。如果 eval 和 文字轉 JavaScript - 對應用程式至關重要 將 'unsafe-eval' 新增為 script-src 中的允許來源,即可啟用這些元件 指令,但我們強烈建議不要這麼做。禁止執行 字串讓攻擊者更難執行未經授權的操作 導入大量程式碼

報表

CSP 能夠封鎖不受信任的資源,在用戶端對 但利用這種模式 會傳回至伺服器,方便您找出所有可能產生 避免惡意插入惡意內容為此,您可以指示 瀏覽器匯出至 POST JSON 格式的違規報告,回報為一個位置 在 report-uri 指令中指定的位置。

Content-Security-Policy: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

報表看起來會像這樣:

{
  "csp-report": {
    "document-uri": "http://example.org/page.html",
    "referrer": "http://evil.example.com/",
    "blocked-uri": "http://evil.example.com/evil.js",
    "violated-directive": "script-src 'self' https://apis.google.com",
    "original-policy": "script-src 'self' https://apis.google.com; report-uri http://example.org/my_amazing_csp_report_parser"
  }
}

這提供了大量資訊 具體原因,包括違規網頁 發生 (document-uri),該網頁的參照網址 (請注意,與 HTTP 標頭欄位,金鑰「沒有」拼字錯誤),表示該資源違反 網頁的政策 (blocked-uri),也就是網頁違反的具體指令 (violated-directive) 和網頁的完整政策 (original-policy)。

報表專用

如果您才剛開始使用 CSP,可以評估目前的 瞭解應用程式狀態,再向使用者推出圖像政策。 為了逐步完成部署作業,您可以要求瀏覽器 並檢舉違規行為,但不強制執行限制。而不是 傳送 Content-Security-Policy 標頭,則傳送 Content-Security-Policy-Report-Only 標頭。

Content-Security-Policy-Report-Only: default-src 'self'; ...; report-uri /my_amazing_csp_report_parser;

在「報表專用」模式中指定的政策不會封鎖受限制的資源,但 它會將違規報表寄到您指定的地區。您甚至可以 同時強制執行一項政策,同時監控另一個政策。太棒了 可評估應用程式的 CSP 變更所造成的影響:開啟 檢舉新政策、監控違規檢舉,並修正 調高;等一切沒問題後,就可以開始強制執行新政策。

實際使用情況

CSP 1 可在 Chrome、Safari 和 Firefox 中使用,但功能有限 支援 IE 10。你可以 查看 caniuse.com 的詳細資訊。CSP 等級 2 自從 Chrome 開始於 Chrome 推出 第 40 版。像 Twitter 和 Facebook 等大型網站都已部署標題 (Twitter 的個案研究值得一讀),相關標準也非常準備就緒 讓您開始在自家網站上部署

制定應用程式政策的第一步,就是評估 查看實際載入的資源確定自己對 以及設定相關的政策 Google Cloud 就是最佳選擇以下我們將逐一介紹幾個常見用途 而他們最好能在 CSP 的保護範圍內予以支援。

用途 1:社群媒體小工具

  • Google 的 +1 按鈕 含有 https://apis.google.com 的指令碼,並嵌入了 <iframe>https://plusone.google.com。您需要一個同時包含這兩者的政策 字串內,才能嵌入按鈕。最低政策為 script-src https://apis.google.com; child-src https://plusone.google.com。您也需要 確保 Google 提供的 JavaScript 程式碼片段能擷取至 外部 JavaScript 檔案如果您已使用 frame-src 套用第 1 級政策 第 2 級需要您將其變更為 child-src。不再需要這項操作 第 3 級

  • Facebook 的喜歡按鈕 提供多種實作選項建議您繼續使用 <iframe> 版本,因為該版本會與您網站的其他部分安全採用沙箱機制。這項服務 需要 child-src https://facebook.com 指令才能正常運作。注意事項 在預設情況下,Facebook 提供的 <iframe> 程式碼會載入相對 網址://facebook.com。請將 變更為明確指定 HTTPS: https://facebook.com。如果您沒有需要,也可以使用 HTTP。

  • Twitter 的推文按鈕 必須透過存取指令碼和影格,兩者都是由 https://platform.twitter.com。(Twitter 也會透過 default;請編輯程式碼,在複製/貼上本機檔案時指定 HTTPS)。 只要移動 JavaScript 程式碼片段,即可透過 script-src https://platform.twitter.com; child-src https://platform.twitter.com 完成 提供給外部 JavaScript 檔案的網址

  • 其他平台的規定類似,也能採取類似的處理方式。 建議您只將 default-src 設為 'none',然後觀察控制台 然後決定需啟用哪些資源,小工具才能正常運作。

加入多個小工具的方法相當簡單明瞭:只要結合政策 指令,記得將同一類型的所有資源合併成單一 指令。如果想要全部三個社群媒體小工具,政策: 輸入:

script-src https://apis.google.com https://platform.twitter.com; child-src https://plusone.google.com https://facebook.com https://platform.twitter.com

用途 2:鎖定

假設您經營銀行網站的某段時間能夠確保 只能載入您自己編寫的資源。在這個情境中 從封鎖所有內容 (default-src 'none') 的預設政策開始,然後再從該政策開始建構。

假設銀行從 CDN 載入所有圖片、樣式和指令碼 https://cdn.mybank.net,並透過 XHR 連線至 https://api.mybank.com/ 到 會將許多位元的資料拆下使用頁框,但僅適用於 網站 (無第三方來源)。網站上沒有 Flash,也沒有字型,也沒有 額外項目。以下是我們可傳送的最嚴格 CSP 標頭:

Content-Security-Policy: default-src 'none'; script-src https://cdn.mybank.net; style-src https://cdn.mybank.net; img-src https://cdn.mybank.net; connect-src https://api.mybank.com; child-src 'self'

用途 3:僅限 SSL

一場婚禮討論論壇管理員想確保所有資源都投入 只透過安全管道載入,但不太撰寫太多程式碼。重新編寫 第三方論壇軟體的大量程式碼 內嵌指令碼和樣式超乎想像。下列政策會是 有效:

Content-Security-Policy: default-src https:; script-src https: 'unsafe-inline'; style-src https: 'unsafe-inline'

雖然 https: 已在 default-src 中指定,但指令碼和樣式 指令不會自動繼承該來源。每項指令 會覆寫該特定資源類型的預設值。

未來

內容安全政策第 2 級為 候選建議。W3C 的 Web 應用程式安全性工作小組 已經開始進行規範的下個疊代作業 內容安全政策第 3 級

如果你有興趣瞭解這些即將推出的功能 略過 public-websec@ 郵寄清單封存封存檔, 或自己加入。

意見回饋