引言:一个让前端老手集体破防的 CORS 报错
你是一个熟悉 JavaScript 与 Web 前端开发的老手,React 写了三年,Vue 用了五年,fetch 请求闭着眼都能写。但第一次接触 Chrome 扩展开发时,你被一个看似简单的架构决策卡住了——为什么我的 Content Script 发起的 API 请求被 CORS 拦下来了?
// content_script.jsconsttoken=awaitchrome.storage.local.get('token');fetch('https://api.crm.com/user',{headers:{Authorization:`Bearer${token}`}});运行,然后在控制台看到一个熟悉的报错:
Access to fetch at 'https://api.crm.com/user' from origin 'https://web.whatsapp.com' has been blocked by CORS policy更让人头疼的是,你刚学会 Manifest V3 中“后台脚本”变成了“Service Worker”,就听说 Chrome 会在闲置 30 秒后把它杀掉。那我的 WebSocket 长连接怎么办?Token 会不会丢?跨域请求到底怎么搞?
本文将从 MV3 的实际约束出发,系统性地剖析 Chrome 扩展中 CORS 绕过的两种核心策略——background.js 代理与原生消息传递(Native Messaging),并深入探讨架构设计、安全风险、竞品对比与生态工具。
一、先认识三个核心角色:Chrome 扩展的“阶级划分”
在深入 CORS 绕过之前,必须先理解 Chrome 扩展中不同组件的权限差异。
| 角色 | 运行环境 | 可访问 DOM | 可绕过 CORS |
|---|---|---|---|
| Background Service Worker | 扩展独立后台 | ❌ | ✅(通过 host_permissions) |
| Content Script | 宿主页面 DOM | ✅ | ❌ |
| Popup / Options | 独立页面 | ✅(自身页面) | ❌ |
关键记忆:Content Script 住在别人家里,Background SW 住在扩展自己的房间里。
这个“阶级划分”是整个 CORS 绕过策略的底层逻辑——不同组件拥有完全不同的网络请求权限。
二、MV3 的 CORS 铁律:为什么你绕不开 Background
2.1 Content Script 的 CORS 困境
Content Script 的fetch()遵循宿主页面的源(Origin)。当它请求一个跨域 API 时,浏览器会发送 CORS 预检,并要求目标服务器返回允许该宿主域的Access-Control-Allow-Origin头。
绝大多数情况下,你无法控制第三方 API 后端去允许所有可能的宿主页面(如web.whatsapp.com、mail.google.com等)。这就是为什么 Content Script 直接发请求几乎总是被 CORS 拦截。
2.2 Background Service Worker 的特权
Background Service Worker 完全不同。它在扩展自己的上下文(chrome-extension://<extension-id>)中运行,并且你在manifest.json中声明了host_permissions后,Background 发起的请求完全绕过 CORS 检查——浏览器信任扩展,如同信任本地系统服务。
// manifest.json{"manifest_version":3,"host_permissions":["https://api.crm.com/*","https://api.openai.com/*"]}配置了这个之后,你的 Service Worker、popup 页面或者 options 页面里的 fetch 请求,只要目标是声明中的域名及其子路径,就都会被放行。
2.3 核心结论
无论 Token 放在哪里,所有跨域 API 调用都必须经由 Background 代理——这不是一个选项,而是一个强制约束。在 MV3 中,所有跨域 API 调用都必须经过 Background SW 代理——这不是“最佳实践”的选择题,而是“能不能工作”的是非题。
三、策略一:background.js 代理——最经典的 CORS 绕过方案
3.1 架构设计:消息驱动的代理模式
Background 代理的核心架构是一个消息驱动的请求转发系统:
Content Script / Popup ↓ chrome.runtime.sendMessage({ type: 'PROXY_FETCH', url, options }) Background Service Worker ↓ fetch(url, options) —— 绕过 CORS ↓ 返回响应 Content Script / Popup ↓ 收到响应数据3.2 完整代码实现
Background Service Worker(background.js):
// background.js - Manifest V3 Service Workerchrome.runtime.onMessage.addListener((message,sender,sendResponse)=>{if(message.type==='PROXY_FETCH'){const{url,options={}}=message.payload;// 直接 fetch——绕过 CORSfetch(url,{...options,headers:{...options.headers,// 可以在这里统一注入 Token}}).then(asyncresponse=>{constdata=awaitresponse.json();sendResponse({success:true,data,status:response.status});}).catch(error=>{sendResponse({success:false,error:error.message});});returntrue;// 保持消息通道开放以支持异步响应}});Content Script(content_script.js):
// content_script.jsfunctionproxyFetch(url,options={}){returnnewPromise((resolve,reject)=>{chrome.runtime.sendMessage({type:'PROXY_FETCH',payload:{url,options}},(response)=>{if(chrome.runtime.lastError){reject(newError(chrome.runtime.lastError.message));return;}if(response.success){resolve(response.data);}else{reject(newError(response.error));}});});}// 使用方式——和普通 fetch 一模一样!constuserData=awaitproxyFetch('https://api.crm.com/user');3.3 真实案例:FranzAI Bridge V2
根据 Extpose 平台上的公开信息,FranzAI Bridge V2 是一个开发者工具 Chrome 扩展,它通过扩展后台 Service Worker 路由 API 请求,从而绕过浏览器 CORS 限制访问任何端点。其核心特性包括:
- CORS 绕过:通过扩展后台 Service Worker 路由 API 请求
- API Key 注入:按域名安全地注入 API 密钥,密钥保留在扩展中,永不暴露在客户端 JavaScript 中
- 流式支持:完整支持 SSE 和流式响应,兼容 OpenAI、Gemini、Claude 及任意流式 API
- 请求检查器:内置侧边栏实时显示所有请求的状态码、耗时、请求头和响应体预览
这个案例证明了 Background 代理方案在生产环境中的可行性和成熟度。
3.4 Token 管理的三层架构
根据 CSDN 上近期(2026年6月)的深度分析,Token 管理在 Background 代理架构中有三种方案:
方案 A:Token 隔离在 Background(推荐)
- Token 持久化存在
chrome.storage(跨上下文、可同步) - 内存缓存:SW 存活时直接使用内存变量
- 冷启动恢复:SW 被终止后重新启动时,从 storage 重新读取
方案 B:Token 在 Content Script 直接使用(不可行)
- 即使忽略 CORS,Token 会在消息中明文传递(理论上可被其他扩展监听)
方案 C:混合方案(Token 每次由 CS 传给 BG)
- 可行但啰嗦:每次请求都要从 storage 异步读取,增加延迟
四、策略二:Native Messaging——从浏览器到操作系统的桥梁
当 Background 代理还不够用时——比如需要访问本地文件系统、调用系统级 API、或与本地服务通信——Native Messaging就是终极方案。
4.1 什么是 Native Messaging?
根据 Chrome 官方文档,Native Messaging 允许扩展与本地安装的可执行程序进行双向通信。Chrome 将每个 Native Messaging Host 启动在独立进程中,通过标准输入(stdin)和标准输出(stdout)与它通信。
Native Messaging channels始终强制使用 JSON 序列化。尝试向原生宿主发送仅结构化克隆支持的类型(如BigInt)会在消息离开扩展上下文之前失败。
4.2 架构设计:扩展 ↔ 本地宿主
Chrome 扩展 ↓ chrome.runtime.connectNative('com.example.native_host') Native Messaging Host(独立进程) ↓ stdin / stdout 本地系统资源(文件、硬件、本地服务)4.3 完整实现步骤
第一步:编写 Native Messaging Host(以 Node.js 为例)
// native_host.jsconstreadline=require('readline');constrl=readline.createInterface({input:process.stdin,output:process.stdout,terminal:false});// 读取消息(Chrome 使用 4 字节长度前缀 + JSON)letbuffer=Buffer.alloc(0);rl.on('line',(line)=>{try{constmessage=JSON.parse(line);console.error(`[Native Host] Received:${JSON.stringify(message)}`);// 处理请求constresponse={type:'response',payload:{echo:message.payload,timestamp:Date.now()}};// 发送响应(同样需要 4 字节长度前缀)constresponseStr=JSON.stringify(response);constlengthBuffer=Buffer.alloc(4);lengthBuffer.writeUInt32LE(Buffer.byteLength(responseStr),0);process.stdout.write(Buffer.concat([lengthBuffer,Buffer.from(responseStr)]));}catch(e){console.error(`[Native Host] Error:${e.message}`);}});第二步:创建 Native Messaging Host Manifest
// com.example.native_host.json{"name":"com.example.native_host","description":"Example Native Messaging Host","path":"/usr/local/bin/native_host.js","type":"stdio","allowed_origins":["chrome-extension://YOUR_EXTENSION_ID/"]}第三步:安装 Native Messaging Host
根据平台不同,manifest 文件需要放到特定目录:
| 平台 | 路径 |
|---|---|
| Linux | ~/.config/[browser-name]/NativeMessagingHosts/ |
| macOS | ~/Library/Application Support/[Browser]/NativeMessagingHosts/ |
| Windows | %APPDATA%\[Browser]\NativeMessagingHosts\ |
第四步:扩展中连接 Native Host
// background.jsletnativePort=null;functionconnectToNativeHost(){if(nativePort)return;nativePort=chrome.runtime.connectNative('com.example.native_host');nativePort.onMessage.addListener((message)=>{console.log('Received from native host:',message);// 处理来自本地宿主的消息});nativePort.onDisconnect.addListener(()=>{console.error('Native host disconnected:',chrome.runtime.lastError);nativePort=null;// 尝试重连setTimeout(connectToNativeHost,5000);});// 发送消息到本地宿主nativePort.postMessage({type:'request',payload:{action:'readFile',path:'/tmp/data.txt'}});}chrome.runtime.onStartup.addListener(connectToNativeHost);4.4 2026 年 Native Messaging 生态:MCP 与 AI Agent 集成
2026 年,Native Messaging 最引人注目的应用场景是MCP(Model Context Protocol)与 AI Agent 的集成。
根据 npm 上的公开包信息,@zyzheal/chrome-mcp通过 Chrome Native Messaging 协议与 Chrome 扩展进行双向通信,提供 MCP 服务,支持 AI Agent 通过 stdio 协议控制浏览器。其架构为:
AI Agent (Claude/Cursor) ↓ (stdio 协议) mcp-chrome-stdio (MCP 代理) ↓ (HTTP Streamable) Chrome 扩展中的 MCP 服务器 ↓ (Native Messaging) chrome-mcp (本地服务) ↓ Chrome 浏览器操作另一个开源项目mcp-chrome-bridger提供了类似的 Native Messaging Host,支持 Chrome 和 Chromium 在 Linux、macOS 和 Windows 上的多浏览器注册。其 CLI 工具支持自动检测并注册所有已安装的浏览器。
@neurodock/native-host(2026年6月19日发布)则展示了另一个方向——通过 Native Messaging 将本地配置文件(~/.neurodock/profile.yaml)暴露给浏览器扩展,支持 Chrome、Chromium、Brave、Edge、Vivaldi 和 Firefox 等多浏览器。
这意味着:Native Messaging 正在成为连接 AI 世界与浏览器世界的核心桥梁。
五、安全风险:特权越大,责任越大
CORS 绕过能力是一把双刃剑。Background 代理和 Native Messaging 赋予扩展的巨大权限,如果被恶意利用,后果不堪设想。
5.1 近期安全漏洞(2026 年)
CVE-2026-12456:根据 VulDB 和 NVD 的记录,Google Chrome 149.0.7827.155 之前的版本存在一个扩展程序漏洞。攻击者通过诱使用户安装恶意 Chrome 扩展,可以绕过同源策略(Same Origin Policy)。该漏洞被归类为“不恰当实现”(Inappropriate implementation)。修复版本为149.0.7827.155或更高版本。
CVE-2026-11048:另一个影响 Chrome 扩展组件的跨域策略漏洞,于 2026 年 6 月 5 日被记录。
CVE-2026-11212:该漏洞源于 Chrome DevTools 中策略执行不足,使恶意扩展能够绕过跨域数据限制。成功利用此漏洞的攻击者可以泄露跨域数据,可能暴露用户访问的其他网站或 Web 应用中的敏感信息。
5.2 阿里云漏洞库披露的深层问题
根据阿里云漏洞库的信息,Chrome 扩展模块负责运行扩展程序并控制网络请求行为。在受影响版本中,extensions/browser/url_loader_factory_manager.cc的ShouldRelaxCors函数存在逻辑错误。
更严重的是,当渲染进程被入侵后,攻击者可通过伪造参数绕过同源策略限制,伪装成其他扩展身份注册事件监听器,从而访问本不属于该扩展的 origins。修复版本中通过在EventRouter中添加多项授权检查来消除该漏洞。
5.3 安全实践建议
- 最小权限原则:
host_permissions只声明必需的域名,不要使用"<all_urls>" - Native Messaging Host 验证:Native Host 应验证调用它的扩展 ID
- 敏感数据不落地:Token 等敏感信息存储在
chrome.storage.local,不要写入本地文件 - 及时更新 Chrome:保持浏览器在最新版本(≥ 149.0.7827.155)
- CSP 策略:在 manifest.json 中配置严格的 Content Security Policy
六、架构设计深度对比
6.1 两种策略的全面对比
| 维度 | Background 代理 | Native Messaging |
|---|---|---|
| 权限范围 | 仅限网络请求(HTTP/HTTPS) | 完整操作系统访问 |
| 部署复杂度 | 低(纯 JS,无需额外安装) | 高(需安装本地可执行文件) |
| 跨平台支持 | 天然跨平台 | 需为每个平台编译/打包 |
| 性能开销 | 极低(消息传递 + fetch) | 中等(进程间通信) |
| 适用场景 | API 代理、CORS 绕过 | 文件系统、硬件、本地服务 |
| 安全风险 | 中(CORS 绕过可能被滥用) | 高(完整系统访问) |
| 调试难度 | 低(Chrome DevTools 可直接调试) | 高(需查看本地日志) |
6.2 何时选择 Background 代理?
- 只需要调用第三方 HTTP API
- 希望扩展轻量、易于分发
- 不需要访问本地系统资源
- 目标用户是非技术用户(不愿安装额外软件)
6.3 何时选择 Native Messaging?
- 需要读写本地文件系统
- 需要调用系统级 API(如硬件设备、系统命令)
- 需要与本地运行的服務(如数据库、AI 模型)通信
- 构建 AI Agent 与浏览器的桥梁(MCP 场景)
- 需要高性能、低延迟的本地计算
七、竞品对比:Chrome vs Firefox 的 CORS 绕过策略
7.1 Firefox 扩展的 CORS 绕过
Firefox 扩展生态中也有类似的 CORS 绕过方案。根据 Mozilla 附加组件商店的信息:
Safe CORS(2026年3月14日发布)允许开发者按标签页绕过 CORS 限制,完全由页面脚本控制。与传统的工具栏切换全局禁用 CORS 不同,Safe CORS 从你的代码中编程式激活——非常适合本地开发和测试。
CORS Unbound(2026年4月19日发布)是一个 Firefox 扩展,它绕过 CORS 限制并直接在浏览器内部重写 HTTP 请求/响应头。它专为本地开发、原型设计、自动化、爬虫和高级调试而设计——完全不需要运行服务器。
Sources to Folders(2026年1月29日发布)则在扩展后台执行 fetch(绕过 CORS),正确下载跨域资源(CDN)。
7.2 Chrome vs Firefox:核心差异
| 维度 | Chrome (MV3) | Firefox |
|---|---|---|
| 后台脚本 | Service Worker(无 DOM,30秒可被终止) | 持久化后台页面(有 DOM) |
| CORS 绕过方式 | host_permissions+ Background SW | 扩展后台页面 + WebRequest API |
| Native Messaging | 支持(JSON 序列化强制) | 支持(但有兼容性差异) |
| 扩展审核 | Chrome Web Store 审核 | 相对宽松 |
7.3 跨浏览器 Native Messaging 的兼容性挑战
根据@guest271314/native-messaging-nodejs的文档,不同操作系统和浏览器在 Native Messaging 实现上存在差异。开发者需要参考 Chrome 不兼容性文档 来处理这些差异。
好消息是,越来越多的 Native Messaging Host 项目开始主动支持多浏览器。@neurodock/native-host明确支持 Chrome、Chromium、Brave、Edge、Vivaldi 和 Firefox。
八、2026 年最新动态:结构化克隆与消息传递的革新
8.1 Chrome 148 的重大更新
根据 Chrome for Developers 官方博客(2026年4月22日发布),从 Chrome 148 开始,扩展开发者可以选择使用结构化克隆算法进行消息序列化,而不是 JSON!
这一现代化方法让你可以在扩展上下文之间发送更复杂的数据类型,而无需手动序列化变通方法。
JSON 序列化的问题:
// 发送 Map——JSON 序列化后变成 {}constmyMap=newMap([['id',123]]);chrome.runtime.sendMessage(myMap);// 另一端收到的是 {}// 变通方案:手动转换constmessage=Array.from(myMap.entries());chrome.runtime.sendMessage(message);// 接收端再转回 Map结构化克隆的解决方案:
// 直接发送 Map——抵达时仍是 Map 实例!constmyMap=newMap([['id',123]]);chrome.runtime.sendMessage(myMap);// 接收端:message 已经是 Map 实例console.log(message.get('id'));// 1238.2 如何启用结构化克隆
在manifest.json中添加一个键即可全局启用:
{"name":"My Extension","version":"1.0","manifest_version":3,"message_serialization":"structured_clone"}如果省略此键,或在 Chrome 148 以下版本,浏览器默认为基于 JSON 的实现。
8.3 支持的更多类型
结构化克隆支持比 JSON 更广泛的类型,包括File、Blob、Set、BigInt、NaN、Infinity、Date和Error对象。
但不支持SharedArrayBuffer和可传输对象(如ArrayBuffer)——尝试发送Uint8Array将发送副本而非传输。
8.4 对 CORS 绕过架构的影响
结构化克隆对 Background 代理架构的直接影响在于:Content Script 与 Background 之间的消息传递可以携带更丰富的数据类型。
例如,在代理请求时,可以直接传递FormData、Blob或File对象,而无需手动序列化:
// Content ScriptconstformData=newFormData();formData.append('file',fileInput.files[0]);// 直接发送 FormData——结构化克隆支持!chrome.runtime.sendMessage({type:'PROXY_FETCH',payload:{url:'https://api.example.com/upload',options:{method:'POST',body:formData}}});这大大简化了文件上传等复杂场景的实现。
九、生态工具与部署方案
9.1 主流开发框架与工具链
WXT Framework:根据 DeepWiki 上的项目文档,Chrome 扩展使用 Manifest V3 并通过 WXT 框架的配置文件进行配置。WXT 定义了权限、入口点以及浏览器自动化和 MCP 服务器功能所需的安全策略。
WXT 正在成为 2026 年 Chrome 扩展开发的事实标准框架,类似于前端开发中的 Vite。
9.2 Native Messaging Host 部署工具
| 工具 | 发布时间 | 核心功能 |
|---|---|---|
@zyzheal/chrome-mcp | 2026-04-15 | MCP 服务 + Native Messaging,AI Agent 控制浏览器 |
mcp-chrome-bridger | 2026-01-24 | 多浏览器注册 + TypeScript 实现 |
@neurodock/native-host | 2026-06-19 | 多浏览器支持(6 种浏览器) |
@guest271314/native-messaging-nodejs | 2026-06-21 | Node.js/Deno/Bun 原生宿主 |
9.3 部署检查清单
Background 代理方案部署:
- ✅ 在
manifest.json中声明host_permissions - ✅ 在
background.js中实现消息监听与 fetch 代理 - ✅ 在 Content Script 中使用
chrome.runtime.sendMessage调用代理 - ✅ 配置
message_serialization: "structured_clone"(Chrome 148+) - ✅ 测试跨域请求是否正常
Native Messaging 方案部署:
- ✅ 编写 Native Messaging Host 可执行文件
- ✅ 创建 Native Host Manifest(JSON)
- ✅ 将 Manifest 安装到系统对应目录
- ✅ 在扩展中使用
chrome.runtime.connectNative连接 - ✅ 使用
chrome-mcp doctor诊断安装问题 - ✅ 使用
chrome-mcp fix-permissions修复权限问题
十、实践建议与趋势判断
10.1 架构选型决策树
是否需要访问本地系统资源? ├── 否 → Background 代理方案 │ ├── 是否需要传输复杂数据类型(Map、File、Blob)? │ │ ├── 是 → 启用 structured_clone(Chrome 148+) │ │ └── 否 → JSON 序列化即可 │ └── Token 管理 → 方案 A(Token 隔离在 Background) │ └── 是 → Native Messaging 方案 ├── 是否需要 AI Agent 集成? │ ├── 是 → 使用 MCP + Chrome Native Messaging │ └── 否 → 自定义 Native Host └── 是否需要多浏览器支持? ├── 是 → 使用 @neurodock/native-host 等成熟方案 └── 否 → 自定义实现10.2 核心实践建议
- 永远不要把 API Key 写在 Content Script 里——它在客户端完全可见
- 所有跨域请求都必须经过 Background——这不是可选项
- Native Messaging Host 必须验证调用者身份——防止其他恶意扩展滥用
- 保持 Chrome 更新到最新版本——至少 ≥ 149.0.7827.155
- 善用结构化克隆——Chrome 148+ 让消息传递更强大
- 使用成熟的 Native Messaging 工具——不要重复造轮子
10.3 2026 年趋势判断
趋势一:MCP + Native Messaging 成为 AI 浏览器自动化的标准路径
从@zyzheal/chrome-mcp、mcp-chrome-bridger到@neurodock/native-host,2026 年上半年涌现了大量将 AI Agent 与 Chrome 扩展通过 Native Messaging 连接的开源项目。这标志着Native Messaging 正在从“小众技术”走向“AI 基础设施”。
趋势二:结构化克隆将重塑扩展消息传递的范式
Chrome 148 引入的结构化克隆支持,使扩展开发者可以告别 JSON 序列化的种种限制。预计在未来 6-12 个月内,主流扩展框架和工具链将全面适配这一特性。
趋势三:跨浏览器 Native Messaging 走向成熟
从仅支持 Chrome,到支持 Chromium、Brave、Edge、Vivaldi 乃至 Firefox,Native Messaging Host 的跨浏览器支持正在快速完善。这为构建“一次开发,多浏览器部署”的扩展提供了可能。
趋势四:安全审查将持续收紧
CVE-2026-12456、CVE-2026-11048、CVE-2026-11212 等一系列 2026 年披露的漏洞表明,Chrome 扩展的安全审查将越来越严格。开发者需要将安全左移——在架构设计阶段就考虑权限最小化、输入验证和通信加密。
结语
Chrome 扩展的 CORS 绕过从来不是一个“技巧”,而是一个架构设计问题。
Background 代理方案让你在 Web 标准框架内解决跨域问题,而 Native Messaging 则打开了从浏览器到操作系统的大门。两者各有适用场景,并非相互替代,而是互补共存。
2026 年的 Chrome 扩展开发生态正在经历深刻的变革——Manifest V3 的全面普及、结构化克隆的引入、MCP 与 AI Agent 的深度融合——每一个变化都在重新定义“最佳实践”的内涵。
记住:权限越大,责任越大。善用 Background 代理与 Native Messaging,但永远把用户安全放在第一位。
本文引用的信息来源包括:Chrome for Developers 官方博客(2026年4月22日)、CSDN 技术社区(2026年6月)、VulDB 漏洞数据库(2026年6月)、NVD 国家漏洞数据库(2026年6月)、npm 包发布信息(2026年4-6月)、Extpose 扩展平台(2026年2月)、Mozilla 附加组件商店(2026年3-4月)等。所有代码示例均基于 Manifest V3 规范。