內容安全策略

使用 WebExtension API 開發的擴充套件預設會應用內容安全策略 (CSP)。這會限制它們可以載入程式碼的來源,例如 <script>,並禁止使用 eval() 等潛在不安全的操作。本文簡要介紹了 CSP 是什麼,預設策略是什麼以及它對擴充套件的意義,以及擴充套件如何更改預設 CSP。

內容安全策略 (CSP) 是一種有助於防止網站無意中執行惡意內容的機制。網站使用伺服器傳送的 HTTP 標頭來指定 CSP。CSP 主要關注指定各種型別內容的合法來源,例如指令碼或嵌入式外掛。例如,網站可以使用它來指定瀏覽器應僅執行來自網站本身的 JavaScript,而不是來自任何其他來源。CSP 還可以指示瀏覽器禁止使用 eval() 等潛在不安全的操作。

與網站一樣,擴充套件也可以從不同來源載入內容。例如,瀏覽器操作的彈出視窗被指定為一個 HTML 文件,並且它可以像普通網頁一樣包含來自不同來源的 JavaScript 和 CSS。

html
<!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_policy manifest.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)

例如,考慮擴充套件文件中的這一行:

html
<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 進行評估。這意味著以下是不允許的:

js
eval("console.log('some output');");
js
setTimeout("alert('Hello World!');", 500);
js
const f = new Function("console.log('foo');");

內聯 JavaScript

在預設 CSP 下,內聯 JavaScript 不會被執行。這會禁止直接放在 <script> 標籤中的 JavaScript 和內聯事件處理程式,這意味著以下是不允許的:

html
<script>
  console.log("foo");
</script>
html
<div onclick="console.log('click')">Click me!</div>

如果您目前使用 <body onload="main()"> 之類的程式碼在頁面載入時執行指令碼,請改用監聽 DOMContentLoadedload 事件。

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.jsonhost_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)。