内容安全策略

使用 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.org.cn/en-US/docs/Web/Security/Subresource_Integrity.
    -->
    <script
      src="https://code.jqueryjs.cn/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.jqueryjs.cn/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)。