Content Security Policy(CSP)是伺服器用回應標頭告訴瀏覽器「這個頁面可以載入哪些來源的資源、可以往哪裡送資料」的一組規則,由瀏覽器執行。它承擔的是注入成立之後的後果限制:攻擊者的程式碼已經跑在頁面上,而它載入外部腳本、把竊得的資料送去自己的伺服器這些動作會被政策擋掉。防止注入本身是另一件事,走輸出處理,見 cross-site scripting

概念位置

它在防護分層上是第二層而非第一層,這個定位決定了怎麼評估它。第一層(按資料落點編碼輸出)失效時第二層才起作用,因此拿 CSP 當唯一防線等於接受「注入一定會發生」並只求降低後果——那是合理的假設,但它不能取代第一層。反過來,只做第一層而沒有第二層時,任何一個漏掉的落點都直接等於完整的能力交出。

它與 same-origin policy 的分工要分清楚:同源政策是瀏覽器的預設隔離,管的是「別的來源讀不讀得到這個來源的回應」;CSP 是這個來源自己宣告的額外限制,管的是「這個頁面自己可以載入與送出什麼」。前者保護別人不讀到我,後者限制我自己能做的事。

可觀察訊號與例子

政策的實際效力由它有多寬決定,而寬到失去意義的形態很具體:允許行內腳本執行、或把來源清單放到涵蓋大型公用託管服務。前者讓注入的腳本直接跑起來,後者讓攻擊者把自己的程式碼放上那個服務再從那裡載入。這兩種寫法通常出自「加了政策之後頁面壞掉」的除錯過程,於是政策存在而保護不存在。

導入的順序是另一個訊號。先用純回報模式(違規只上報、不阻擋)跑一段時間,收到的違規清單就是既有頁面實際依賴哪些來源的盤點;直接進強制模式的系統會在上線當下壞掉一批功能,而搶修的方向多半是放寬政策。

第三方腳本在這裡與 cross-site scripting 是同一個問題的兩面:頁面上每掛一個外部腳本,政策就要多開一個來源,而那份程式碼的內容由對方的發佈流程決定。

設計責任

政策要與頁面的實際依賴一起維護,而不是寫一次就放著。新增第三方元件時政策要跟著改,這件事要接進發佈流程——沒接的話,開發者遇到被擋的資源時會就地放寬,而放寬不會有人複查。

違規回報要收集起來並有人看。它同時是兩種訊號:政策與實際依賴不符(該修政策),以及頁面上出現了不該出現的來源(該查注入)。兩者的處置相反,因此回報要能區分,而不是併成一個計數。

憑證的住址要跟這一層一起決定。系統若把憑證放在 JavaScript 讀得到的位置,那一份的安全水準就建立在「這個頁面不會跑到別人的程式碼」這個前提上,而維持那個前提的成本由前端承擔——沒有人承擔時,這個決定要重判,見 7.36 憑證在請求中怎麼帶