禁止远程代码与发布规范
本教程共 56 篇 · 第 45 篇 · 更新于 2026-08-13 · 约 9 分钟阅读
本节目标:学完你能判断哪些写法会被判定为”远程托管代码”、知道把所有逻辑打包进扩展的合规替代方案,并了解商店审查时主要卡哪些安全关。
上一节讲的是技术层面:CSP 把扩展能执行的代码锁在包内部。这一节讲制度层面:Chrome Web Store 在审核时,会专门检查你的扩展有没有”运行时从外部拉代码”。两条线合起来,就是 Manifest V3 最核心的一条安全铁律——所有会执行的 JS,必须随扩展包一起提交、一起过审。
45-1 什么是远程托管代码
“远程托管代码”(remotely hosted code),指的是扩展在用户浏览器里运行时,从扩展包之外的地址获取并执行的 JavaScript。注意关键词:“执行”。从服务器拉数据、拉配置、拉图片,这些都不算;算的是”拉回来一段能跑的代码再执行它”。
Chrome 的禁令非常明确:扩展包里必须包含它会执行的所有代码。审查团队审的是你提交的那个包,用户装的也必须是那个包。中间不能有任何”运行时再悄悄下载一段脚本”的口子。
这条规则是整个 MV3 安全模型的基石。没有它,CSP 再严也只是纸面文章——攻击者完全可以提交一份干净包过审,然后让扩展运行时从自己服务器拉恶意代码。把远程代码这条路堵死,审查才真正有意义。
45-2 哪些写法会踩线
下面几类,是商店审核时重点盯、也最容易让新手翻车的”远程托管代码”形态:
用 eval / new Function 执行远程字符串。 比如先 fetch 一段脚本文本,再 eval 它;或者用 new Function(远程字符串)。这是最典型的违规,eval 在扩展里本来就被 CSP 封死,配合远程来源更是双重违规。
动态加载远程 <script>。 在页面里插入 <script src="https://你的服务器/xxx.js">,或从 CDN 引入库。上一节讲过 CSP 会拦,这里再说一遍:它不仅技术上跑不起来,提交商店也会被直接打回。
运行时从服务器取代码注入页面。 比如用 chrome.scripting.executeScript 的 code 字段,传进去的字符串是从远程接口现拉的。只要那段代码不是写死在你提交的文件里,就违规。
靠远程配置改变扩展行为逻辑。 这是更隐蔽的一类。代码本体在包里,但关键分支、功能开关完全由远程下发的”代码段”决定。审核认定:实际行为不在提交物中,等于变相远程托管。
Warning有个误区要避开:有人以为”我把代码放进沙盒 iframe 从远程加载就行”。不行。沙盒只是隔离环境,不能成为绕开远程代码禁令的后门。沙盒里跑的代码同样得在包内。
45-3 第三方库必须跟着打包
很多项目离不开 jQuery、某个工具库,或者 React 之类的框架。在 MV3 下,正确做法是:把库的 .js 文件下载进扩展目录,像引用自己代码一样引用它。
<!-- 正确:库文件随扩展打包,放在扩展目录里 -->
<script src="./vendor/jquery.min.js"></script>
<script src="./content-script.js"></script>
{
"manifest_version": 3,
"name": "My extension",
"content_scripts": [
{
"matches": ["https://example.com/*"],
"js": ["vendor/jquery.min.js", "content-script.js"]
}
]
}
这样库和你的代码一起提交、一起过审,来源完全可见。千万别图省事写 https://code.jquery.com/...,那会被当成远程托管直接拒。
向页面注入脚本时,用 chrome.scripting.executeScript 的 files 数组,把要注入的文件放在扩展包内:
chrome.scripting.executeScript({
target: { tabId: tab.id },
files: ['vendor/jquery.min.js', 'content-script.js']
});
这就合规——文件是打包进去的,不是运行时现拉的。
45-4 想动态执行?用 func 而不是 code 字符串
如果你需要”针对不同情况注入不同逻辑”,别用 code 传一段拼接出来的字符串。MV3 提供了更安全的 func + args 方式:函数本体写在你的源码里(随包提交),运行时的可变部分只通过 args 当数据传进去。
async function getCurrentTab() {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
return tab;
}
let tab = await getCurrentTab();
function showAlert(givenName) {
alert(`Hello, ${givenName}`);
}
let name = 'World';
chrome.scripting.executeScript({
target: { tabId: tab.id },
func: showAlert,
args: [name]
});
这里 showAlert 是写死在提交文件里的函数,name 只是普通数据。可执行的逻辑始终在包内,符合要求。对比一下:如果你把 showAlert 的内容改成从接口拉、再拼成 code 字符串传进去,那就违规了。
Tip一句话记牢:
func传函数、args传数据,逻辑永远写在提交的源码里;code字段只适合传静态、写死的字符串,绝不接远程内容。
45-5 合规的三类替代思路
当你的需求原本依赖”远程代码”时,下面三种思路是 Chrome 官方推荐的合规替代,按场景选用。
配置驱动,逻辑本地。 服务器只下发数据或配置(比如开关、阈值、文案),真正判断和执行的分支写在扩展本地。扩展读到配置后,用本地代码做决定。数据可变、逻辑不变,行为仍在审查范围内。
逻辑外置成远程服务。 别让扩展自己算,把”算”搬到你的服务器,扩展只负责调用接口拿结果。比如要翻译一段文字,扩展把原文发给翻译服务,拿回译文展示——它执行的是”发起请求、渲染结果”的本地代码,翻译能力在远端服务里,这不叫远程托管代码。
不可信代码放沙盒。 确实需要执行来源不完全可控的代码(如用户模板),放进沙盒页隔离运行,且沙盒代码也必须打包在扩展内,不能从远程拉。
这三种的共同点:最终在浏览器里 eval、执行的脚本,全部在提交包中可见。变的是数据,不是代码。
45-6 商店审查关注哪些安全关
远程代码禁令只是审核的一关。把扩展提交到 Chrome Web Store 时,审查还会重点看下面这些安全要求。提前心里有数,能少走很多弯路。
单一用途原则。 扩展的功能要聚焦、明确,且和商店详情页描述一致。一个”网页截图”扩展突然要去读你的全部浏览记录,会被质疑。权限和功能要对得上。
权限最小化。 申请的每个权限都要能说清用途。申请了 history 却只用来看标题,审查会要求你删掉多余权限。前面章节讲的最小权限原则,在这里是硬指标。
代码可读性。 提交的可执行代码应当可以被审查。过度混淆、刻意让逻辑难以辨认的代码会被要求提供可读源码。正常的压缩(minify)一般没问题,但用混淆手段故意藏逻辑,会被打回。
用户数据处理。 涉及用户数据(账号、浏览行为、位置等),必须有清晰的披露:收集什么、为什么、怎么用、是否外传。隐私政策该有的要有,且要和实际行为一致,不能挂羊头卖狗肉。
功能与描述一致。 扩展实际做的事不能超出、偏离你在详情页写的功能。诱导下载、暗藏额外行为,都是拒绝理由。
Note这些要求不是写了清单就能过,而是”你的代码 + 描述 + 权限 + 隐私说明”要自洽。审查看的是整体画像,不是单看某个字段。
45-7 哪些做法最容易被打回
结合上面几节,把高频”被拒原因”列出来,写的时候绕着走:
- 运行时
eval或new Function执行来自网络的字符串。 - 从 CDN 或自有服务器动态加载脚本文件。
- 提交的是混淆到无法审查的代码,又给不出可读版本。
- 申请了大量用不上的权限,和功能明显不匹配。
- 详情页描述轻描淡写,扩展实际在后台做别的事。
- 把关键功能逻辑藏在远程下发的”配置/脚本”里。
Tip如果你拿不准某段写法算不算远程托管,用这个自检标准:把扩展包解压出来,里面是不是能找到用户运行时会执行的全部 JS?能找到,就合规;找不到(要靠联网才拿到),就违规。
45-8 落到工程习惯上
把这一节和上一节合起来,好的扩展工程习惯是这样的:
- 所有 JS 进包,第三方库下载到
vendor/再引用,不挂外链。 - 动态逻辑用
func+args,或”远程取数据、本地算结果”。 - 清单里
content_security_policy保持默认或只做有限收紧,绝不放开远程源和 eval。 - 申请权限时逐条想清楚用途,能不申请的就不申请。
- 提交前自查:包内能否找到全部会执行的代码。
说实话,这些约束刚接触会觉得束手束脚。但换成用户视角就明白:一个能读你账号、能改你网页的扩展,如果还能偷偷从服务器拉未知代码,那风险是不可控的。Chrome 用技术(CSP)加流程(审核禁令)双重锁死它,护的是所有装扩展的人。你顺着这套规则写,反而省掉了自己兜底安全的海量工作。
到这一节,模块九”安全与策略”就讲完了。下一模块我们进入调试:怎么把扩展加载进浏览器、怎么在各组件里排错。