Chrome扩展MV3中如何通过内容脚本执行任意用户配置JS?
问题背景
我维护的一款Chrome扩展(原MV2版本)核心功能是允许用户为不同域名保存自定义JS/CSS,通过内容脚本加载后注入页面DOM,让JS运行在页面上下文(而非内容脚本隔离上下文),以此访问页面的全局变量(如jQuery)。升级到MV3后,所有直接注入inline script的方式都会触发CSP错误,即使目标网站本身没有设置CSP,报错如下:
Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval'". Either the 'unsafe-inline' keyword, a hash ('sha256-AlO1/1Id4iveKnVIfYjiiyBnWJi9Q+yh6WC1bMbRbFk='), or a nonce ('nonce-...') is required to enable inline execution.
尝试过的方法均无效:
- 沿用MV2方式:内容脚本直接添加含自定义代码的
script标签 → 触发CSP错误 - 通过Service Worker调用
chrome.scripting.executeScript(),在注入函数内创建script标签 → 触发CSP错误 - 以非JS类型注入内容再转换 → 触发CSP错误
- 添加sha256哈希作为
integrity属性 → 无效
可行解决方案
使用chrome.scripting.executeScript()的world: 'MAIN'参数,直接将用户自定义代码注入到页面的全局上下文执行,绕过CSP对inline脚本的限制。
步骤1:配置Manifest
确保manifest.json中声明必要的权限和主机权限:
{ "manifest_version": 3, "permissions": ["scripting", "storage"], "host_permissions": ["<all_urls>"] }
步骤2:实现代码注入逻辑
从chrome.storage获取用户保存的自定义JS,通过scripting API注入到页面的MAIN世界(即页面全局上下文):
示例1:通过eval执行用户代码
// 内容脚本或Service Worker中执行 chrome.storage.local.get(['customJs'], async (result) => { const customJs = result.customJs; if (!customJs) return; const currentTab = await chrome.tabs.getCurrent(); await chrome.scripting.executeScript({ target: { tabId: currentTab.id }, func: (code) => { // 在页面上下文直接执行代码 eval(code); }, args: [customJs], world: 'MAIN' // 关键:指定运行在页面全局上下文 }); });
示例2:用Function构造器执行
// 内容脚本或Service Worker中执行 const currentTab = await chrome.tabs.getCurrent(); await chrome.scripting.executeScript({ target: { tabId: currentTab.id }, func: new Function(result.customJs), // 直接将用户代码作为函数体执行 world: 'MAIN' });
方案原理
world: 'MAIN'参数让代码直接运行在页面的全局上下文,而非扩展的隔离内容脚本上下文- 这种注入方式不属于传统的inline script标签注入,而是通过Chrome扩展API直接在目标上下文执行代码,因此不受CSP对inline脚本的限制
- 代码可以直接访问页面的全局变量(如
window、jQuery等),完全符合原MV2版本的功能需求
注意事项
- 必须确保
host_permissions覆盖目标网站,否则无法调用chrome.scripting.executeScript() - 由于允许用户执行自定义代码,需保留原有的安全提示(如提醒用户仅运行可信代码),避免潜在的XSS风险
- CSS注入可继续沿用MV2的方式(直接添加
style标签),MV3对inline CSS的限制相对宽松,多数无CSP的网站均可正常运行
内容的提问来源于stack exchange,提问作者Rudie

