Chrome扩展Manifest V3指定URL代码注入及CSP错误解决求助
解决Chrome扩展Manifest V3注入样式与脚本的CSP问题
我正在开发一款Chrome扩展,需要向指定URL的网站<head>标签注入样式与脚本,同时避免重复注入。初始实现无效,尝试了三种方案都触发CSP错误,具体细节如下:
初始实现
manifest.json
{ "manifest_version": 3, "name": "Alert Extension", "version": "1.0", "permissions": [ "activeTab", "scripting" ], "background": { "service_worker": "background.js" }, "action": { "default_popup": "popup.html" } }
background.js
chrome.webNavigation.onCompleted.addListener(function(details) { if (details.url.includes('domain.subdomain.com')) { chrome.scripting.executeScript({ target: { tabId: details.tabId }, code: ` if (!document.getElementById("myStyle")) { var style = document.createElement("style"); style.id = "myStyle"; style.textContent = "/* CSS content goes here */"; document.head.appendChild(style); } if (!document.getElementById("myScript")) { var script = document.createElement("script"); script.id = "myScript"; script.textContent = "/* Javascript content goes here */"; document.head.appendChild(script); } ` }); } });
尝试的三种方案及共同错误
所有方案均触发以下CSP错误:
"Refused to execute inline script because it violates the following Content Security Policy directive: "script-src 'self' 'wasm-unsafe-eval' 'inline-speculation-rules' http://localhost:* http://127.0.0.1:*". Either the 'unsafe-inline' keyword, a hash ('sha256-tjq+f9k3DXjCYpN8P19vmoR+gGAU/7ojD5Y2lMnE774='), or a nonce ('nonce-...') is required to enable inline execution."
方案1
manifest.json
{ "manifest_version": 3, "name": "Alert", "version": "1.0", "permissions": [ "tabs", "scripting" ], "background": { "service_worker": "background.js" }, "content_scripts": [ { "matches": ["*://subdomain.domain.com/*"], "js": ["content.js"] } ] }
injected.js
var code = "//Etiqueta de script"; // Code to be injected in the head section var script = document.createElement('script'); var nonce = 'myrandomnonce'; script.setAttribute('nonce', nonce); script.appendChild(document.createTextNode(code)); document.head.appendChild(script);
content.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) { if (request.action === 'injectCode') { chrome.scripting.executeScript({ target: { tabId: sender.tab.id }, files: ['injected.js'] }); } });
background.js
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { if (changeInfo.status === 'complete' && tab.active) { chrome.tabs.query({ active: true, currentWindow: true, url: '*://subdomain.domain.com/*' }, function(tabs) { if (tabs.length > 0) { chrome.tabs.sendMessage(tabId, { action: 'injectCode' }); } }); } });
方案2
manifest.json
{ "manifest_version": 3, "name": "Alert", "version": "1.0", "permissions": [ "tabs", "scripting", "webNavigation" ], "background": { "service_worker": "background.js" } }
background.js
chrome.webNavigation.onCompleted.addListener(function(details) { if (details.frameId === 0) { chrome.tabs.executeScript(details.tabId, { file: "injected.js" }); } });
injected.js
alert('We are in the website');
方案3
manifest.json
{ "manifest_version": 3, "name": "test Alert", "version": "1.0", "permissions": [ "tabs", "scripting" ], "background": { "service_worker": "background.js" }, "content_scripts": [ { "matches": ["*://subdomain.domain.com/*"], "js": ["content.js"] } ] }
content.js
function injectCode() { var code = "//Etiqueta de script"; var script = document.createElement('script'); script.appendChild(document.createTextNode(code)); document.documentElement.appendChild(script); } chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab) { if (changeInfo.status === 'complete' && tab.active) { chrome.tabs.query({ active: true, currentWindow: true, url: '*://subdomain.domain.com/*' }, function(tabs) { if (tabs.length > 0) { chrome.scripting.executeScript({ target: { tabId: tabId }, function: injectCode }); } }); } });
解决方案
核心思路
问题根源是页面CSP禁止内联脚本,你之前的方案都在尝试注入带textContent的内联脚本,直接触发了限制。Manifest V3下正确的规避方式:
- 样式用Chrome提供的
chrome.scripting.insertCSS直接注入外部文件,无需手动创建<style>标签。 - 脚本优先用外部文件通过
chrome.scripting.executeScript加载,避免内联内容;如果需要动态逻辑,用func参数传递函数而非内联代码。 - 用全局变量或自定义属性标记注入状态,避免重复执行。
具体实现
1. 更新manifest.json
{ "manifest_version": 3, "name": "Alert Extension", "version": "1.0", "permissions": ["scripting", "webNavigation"], "host_permissions": ["*://subdomain.domain.com/*"], "background": { "service_worker": "background.js" } }
- 新增
host_permissions明确指定目标网站,避免权限范围模糊。 - 移除不必要的
activeTab、tabs权限。
2. background.js(注入逻辑)
chrome.webNavigation.onCompleted.addListener(async (details) => { // 仅处理主框架且匹配目标域名 if (details.frameId !== 0) return; const tabId = details.tabId; // 注入样式 await chrome.scripting.insertCSS({ target: { tabId }, files: ['inject-style.css'] }); // 注入脚本 await chrome.scripting.executeScript({ target: { tabId }, files: ['inject-script.js'] }); }, { url: [{ hostSuffix: 'subdomain.domain.com' }] });
- 用
webNavigation的url参数提前过滤目标网站,减少冗余判断。 - 用官方API处理样式和脚本注入,完全规避CSP内联限制。
3. inject-style.css(待注入的样式)
/* 自定义样式内容 */ body { border: 2px solid #ff4444; }
4. inject-script.js(待注入的脚本)
// 检查是否已注入,避免重复执行 if (window._extensionInjected) return; window._extensionInjected = true; // 自定义脚本逻辑 console.log("扩展脚本注入成功");
- 用全局变量标记注入状态,简单有效。
- 脚本内容直接写在文件中,通过API加载不会触发CSP限制。
动态脚本替代方案
如果需要动态生成脚本内容,不要用内联文本,改用executeScript的func参数传递函数:
// 在background.js中 await chrome.scripting.executeScript({ target: { tabId }, func: (dynamicParam) => { if (window._extensionInjected) return; window._extensionInjected = true; // 这里可以使用dynamicParam生成动态逻辑 console.log("动态脚本执行:", dynamicParam); }, args: ["动态参数内容"] });
这种方式Chrome会自动处理函数的注入,不会被视为内联脚本,完美绕过CSP限制。
内容的提问来源于stack exchange,提问作者Froster A.
相关产品推荐
相关产品推荐

