內容安全策略
使用 WebExtension API 開發的擴充套件預設會應用內容安全策略 (CSP)。這會限制它們可以載入程式碼的來源,例如 <script>,並禁止使用 eval() 等潛在不安全的操作。本文簡要介紹了 CSP 是什麼,預設策略是什麼以及它對擴充套件的意義,以及擴充套件如何更改預設 CSP。
內容安全策略 (CSP) 是一種有助於防止網站無意中執行惡意內容的機制。網站使用伺服器傳送的 HTTP 標頭來指定 CSP。CSP 主要關注指定各種型別內容的合法來源,例如指令碼或嵌入式外掛。例如,網站可以使用它來指定瀏覽器應僅執行來自網站本身的 JavaScript,而不是來自任何其他來源。CSP 還可以指示瀏覽器禁止使用 eval() 等潛在不安全的操作。
與網站一樣,擴充套件也可以從不同來源載入內容。例如,瀏覽器操作的彈出視窗被指定為一個 HTML 文件,並且它可以像普通網頁一樣包含來自不同來源的 JavaScript 和 CSS。
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
</head>
<body>
<!--Some HTML content here-->
<!--
Include a third-party script.
See also https://mdn.club.tw/en-US/docs/Web/Security/Subresource_Integrity.
-->
<script
src="https://code.jquery.com/jquery-2.2.4.js"
crossorigin="anonymous"></script>
<!-- Include my popup's own script-->
<script src="popup.js"></script>
</body>
</html>
與網站相比,擴充套件具有對附加特權 API 的訪問許可權,因此如果它們被惡意程式碼洩露,風險會更大。出於這個原因
- 預設情況下,對擴充套件應用了一個相當嚴格的內容安全策略。請參閱 預設內容安全策略。
- 擴充套件作者可以使用
content_security_policymanifest.json 鍵來更改預設策略,但允許的策略有限制。請參閱content_security_policy。
預設內容安全策略
使用 Manifest V2 的擴充套件的預設內容安全策略是
"script-src 'self'; object-src 'self';"
而對於使用 Manifest V3 的擴充套件,預設內容安全策略是
"script-src 'self'; upgrade-insecure-requests;"
這些策略適用於任何未透過 content_security_policy manifest.json 鍵顯式設定自己內容安全策略的擴充套件。它有以下後果:
指令碼和物件資源的載入位置
在預設 CSP 下,您只能載入擴充套件本地的程式碼。CSP 將 script-src 限制為僅安全來源,包括 <script> 資源、ES6 模組和 Web Workers。在支援已廢棄的 外掛 的瀏覽器中,object-src 指令也會受到限制。有關擴充套件中 object-src 的更多資訊,請參閱 WECG 問題 Remove object-src from the CSP (at least in MV3)。
例如,考慮擴充套件文件中的這一行:
<script src="https://code.jquery.com/jquery-2.2.4.js"></script>
這不會載入請求的資源:它會靜默失敗,並且任何您期望從資源中獲得的以為會存在的物件都找不到。對此有兩個主要的解決方案:
- 下載資源,將其打包到您的擴充套件中,然後引用該資源的該版本。
- 使用
content_security_policy鍵,或者在 Manifest V3 中使用content_scripts屬性,來允許您需要的遠端來源。
注意:如果修改後的 CSP 允許遠端指令碼注入,您的擴充套件將在審查過程中被 addons.mozilla.org (AMO) 拒絕。有關更多資訊,請參閱 安全最佳實踐 的詳細資訊。
eval() 及其同類函式
在預設 CSP 下,擴充套件不能將字串作為 JavaScript 進行評估。這意味著以下是不允許的:
eval("console.log('some output');");
setTimeout("alert('Hello World!');", 500);
const f = new Function("console.log('foo');");
內聯 JavaScript
在預設 CSP 下,內聯 JavaScript 不會被執行。這會禁止直接放在 <script> 標籤中的 JavaScript 和內聯事件處理程式,這意味著以下是不允許的:
<script>
console.log("foo");
</script>
<div onclick="console.log('click')">Click me!</div>
如果您目前使用 <body onload="main()"> 之類的程式碼在頁面載入時執行指令碼,請改用監聽 DOMContentLoaded 或 load 事件。
WebAssembly
希望使用 WebAssembly 的擴充套件需要在 script-src 指令中指定 'wasm-unsafe-eval'。
從 Firefox 102 和 Chrome 103 開始,可以在 content_security_policy manifest.json 鍵中包含 'wasm-unsafe-eval',以啟用 WebAssembly 在擴充套件中的使用。
Firefox 中的 Manifest V2 擴充套件可以在沒有 CSP 中的 'wasm-unsafe-eval' 的情況下使用 WebAssembly,以實現向後相容。然而,這種行為不能保證,請參閱 Firefox bug 1770909。因此,建議使用 WebAssembly 的擴充套件在其 CSP 中宣告 'wasm-unsafe-eval'。
對於 Chrome,擴充套件在 101 及更早版本中無法使用 WebAssembly。在 102 版本中,擴充套件可以使用 WebAssembly(與 Firefox 101 及更早版本行為相同)。從 103 版本開始,如果擴充套件在其 manifest 鍵的 content_security_policy 中包含 'wasm-unsafe-eval',則可以使用 WebAssembly。
Manifest V3 中升級不安全的網路請求
擴充套件在與外部伺服器通訊時應使用 https: 和 wss:。為了鼓勵這種標準行為,預設的 Manifest V3 CSP 包含 upgrade-insecure-requests 指令。此指令會自動將 http: 的網路請求升級為使用 https:。
雖然請求會自動升級,但仍建議在擴充套件的原始碼中儘可能使用 https:-URL。特別是,manifest.json 的 host_permissions 部分中的條目應以 https:// 或 *:// 開頭,而不是僅以 http:// 開頭。
需要進行 http: 或 ws: 請求的 Manifest V3 擴充套件可以透過在 content_security_policy manifest.json 鍵中使用一個不包含 upgrade-insecure-requests 指令的策略來覆蓋預設 CSP,從而選擇退出此行為。然而,為了符合擴充套件程式 策略的安全要求,所有使用者資料都必須安全傳輸。
內容指令碼的 CSP
在 Manifest V2 中,內容指令碼沒有 CSP。在 Manifest V3 中,內容指令碼與擴充套件共享預設 CSP。目前無法為內容指令碼指定單獨的 CSP(來源)。
CSP 控制內容指令碼載入的程度因瀏覽器而異。在 Firefox 中,eval 等 JavaScript 功能受到擴充套件 CSP 的限制。總的來說,大多數基於 DOM 的 API 都受網頁 CSP 的約束。在 Chrome 中,許多 DOM API 受擴充套件 CSP 的約束,而不是網頁的 CSP(crbug 896041)。