You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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下正确的规避方式:

  1. 样式用Chrome提供的chrome.scripting.insertCSS直接注入外部文件,无需手动创建<style>标签。
  2. 脚本优先用外部文件通过chrome.scripting.executeScript加载,避免内联内容;如果需要动态逻辑,用func参数传递函数而非内联代码。
  3. 用全局变量或自定义属性标记注入状态,避免重复执行。

具体实现

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.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 18:15:04