CSP 頭部驗證工具

貼上 Content-Security-Policy 頭部的值,一次性驗證所有指令和來源值。可檢測 unsafe-inline 等高風險設定、缺失的 default-src 兜底、指令名稱拼寫錯誤,並以視覺化方式展示每個指令允許的來源。

使用技巧

  • CSP 通常以 HTTP 響應頭的形式下發,但也可以通過 <meta http-equiv="Content-Security-Policy"> 標籤設定(不過 report-uri 等部分指令在 meta 標籤中不生效)。
  • 在生產環境正式啟用 CSP 之前,建議先使用 Content-Security-Policy-Report-Only 頭部——它只收集違規報告,方便你在不影響現有功能的前提下評估實際影響。
  • 萬用字元(*)和 'unsafe-inline' 雖然能讓開發更省事,但會削弱 CSP 大部分的 XSS 防護能力,上線前務必收緊這類寬鬆策略。
  • 同一指令重複書寫兩次不會合並取值,只有第一次出現生效。如需追加或覆蓋來源,請把所有值寫在同一條指令中。
  • 瀏覽器 DevTools 控制台會以紅色「Refused to load...」資訊顯示被攔截的資源,除錯過嚴的 CSP 時應首先檢視這裡。

常見問題

CSP 會明確告訴瀏覽器,頁面上允許從哪些來源載入指令碼、圖片、樣式等資源。任何不在白名單內的資源(包括內聯指令碼)都會被阻止載入或執行,從而大幅降低 XSS 攻擊執行攻擊者控制指令碼的風險。

有的——nonce 來源('nonce-<隨機值>')和 hash 來源('sha256-<雜湊值>')都可以實現這一點。只要在指令碼標籤上新增與響應頭中一致的 nonce 屬性,就能只允許這一特定標籤執行,同時繼續攔截其他未經授權的內聯指令碼。

這是因為該資源的來源不在白名單內,被 CSP 攔截了。瀏覽器控制台會顯示類似「Refused to load...because it violates the following Content Security Policy directive」的訊息,其中會標明具體的來源域名——將該來源新增到對應指令的白名單中即可解決。

通常是不夠的。default-src 只是對未顯式設定的指令起兜底作用。像 script-src、object-src 這類高風險指令,應各自明確且嚴格地配置,因為它們代表的攻擊面遠大於其他大多數指令。

兩者都用於指定瀏覽器應將違規報告發送到哪裡,但 report-uri 是較早的指令,直接指向一個上報用的 URL;report-to 則基於更新的 Reporting API,引用的是在另一個 Report-To 頭部中定義好的命名分組。由於兩者的瀏覽器支援情況不同,通常建議同時指定兩者以確保相容性。
ツールくん

閒話 ― CSP 為何誕生:僅靠轉義從來都無法徹底防住 XSS

Content Security Policy 的構想最早可追溯到 2004 年前後 Robert Hansen 提出的想法,隨後從 2008 年左右開始,Mozilla 工程師 Brandon Sterne 主導將其整理成正式規範,最終在 2012 年形成了 W3C 的第一份候選推薦標準(Level 1)。當時跨站指令碼攻擊(XSS)是網路安全領域最緊迫的問題之一,人們逐漸意識到,僅依賴「開發者認真轉義每一處輸出」這種做法並不足以構成穩固的防線。CSP 正是作為一種縱深防禦機制被設計出來,讓瀏覽器本身來強制限制指令碼的合法來源。

CSP Level 2 引入了基於 nonce 和 hash 的內聯指令碼許可機制,使網站無需依賴 'unsafe-inline' 也能允許特定的內聯程式碼執行。隨後 Level 3 又新增了 'strict-dynamic',可以自動信任由已受信指令碼動態載入的子指令碼,大幅降低了大型網站維護基於主機名白名單的成本。

Google 持續在其自身的大規模服務中推行基於 nonce 的嚴格 CSP,並據此釋出研究成果指出:基於主機名的白名單方式經常可以被繞過,而基於 nonce 或 hash 的策略在實踐中有效得多。這一結論目前已被廣泛引用為 CSP 的最佳實踐,也說明真正關鍵的設計決策不只是「禁止哪些取值」,而是「從一開始就該基於哪種許可策略來構建」。