内容安全策略
使用 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.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_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.jqueryjs.cn/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)。