直接往HTML塞多个<script>标签不加隔离必然撞车,因变量覆盖、事件重复绑定、DOM提前操作导致秒级失效;innerHTML+=<script>会同步执行脚本且无作用域封装,污染全局环境。

直接往 HTML 里塞多个 <script> 标签,不加隔离,必然撞车——变量覆盖、事件重复绑定、DOM 提前操作,都是秒级失效。
为什么 innerHTML += <script> 是高危操作
把含 <script> 的字符串拼进 innerHTML,脚本会立即在全局上下文执行,this 指向 window,document 是主页面的,所有副作用都逃不出隔离边界。
- 常见错误现象:
Uncaught TypeError: init is not a function(被后加载脚本覆盖);click事件绑了两遍,点一次触发两次;document.body.classList.add('loading')把整个页面搞卡 - 根本原因:浏览器解析
innerHTML字符串时,会同步执行内联<script>,且无作用域封装 - 哪怕你用
template元素暂存,只要最后调用content.appendChild()或innerHTML,一样触发执行
eval() 和 new Function() 不解决隔离,只放大风险
有人试图用 eval(scriptText) 或 new Function('return ' + scriptText)() 来“控制执行时机”,但这只是把全局执行换了个写法,变量照样污染 window,document 照样可写。
- 真正需要的是上下文重绑定:把
this、window、document、location全部指向沙箱对象,否则fetch('/api')还是发到主站域名 -
eval在 CSP 严格环境下直接被拦截,unsafe-eval不该出现在生产策略里 - Chrome 90+ 已限制
new Function()访问 caller/callee,依赖它的老代码会静默失败
安全注入脚本的最小可行路径:DOMParser + 沙箱执行
想让子应用脚本“只改自己的 DOM、只读自己的数据”,必须拆解、重写、重绑定三步走,不能跳步。
立即学习“前端免费学习笔记(深入)”;
- 用
DOMParser解析 HTML 字符串,提取所有<script src="...">和<script>...</script>节点 - 对每个
src,用fetch()拉取内容,再用new Function(...)构造函数体,但传入沙箱globalThis作为this上下文 - 内联脚本必须剥离并转为函数体,禁止直接
appendChild(scriptEl)——它会立刻执行且无法劫持上下文 - 关键约束:
document必须代理到 shadowRoot 或虚拟 DOM 根节点,localStorage要走 namespaced 封装,否则仍是全局读写
Shadow DOM 下仍需手动拦截 document.write 和 document.writeln
attachShadow({ mode: 'closed' }) 隔离了样式和 DOM 查询,但不拦截全局 API 调用。子应用若含 document.write('<div>hello</div>'),内容仍会写进主文档 <head> 或 <body> 开头,破坏结构。
- 必须在沙箱初始化时,用
Object.defineProperty(document, 'write', { value() {} })覆盖原生方法 -
document.writeln同理,且要检查是否被第三方库(如旧版 jQuery)在运行时动态重赋值 - 更隐蔽的是
document.open()/close()组合,它们会清空整个页面,必须一并拦截并抛出明确错误 - 这些拦截只在沙箱环境生效;主应用代码不受影响,所以得确保子应用脚本真正在沙箱上下文中执行,而非“假装”在 shadow 里
最易被忽略的点:没人检查子应用脚本里有没有 setTimeout(() => document.body.remove(), 100) 这种裸操作——它不经过任何框架拦截,直接生效。隔离不是加个 wrapper 就完事,而是每一行 JS 执行前,都要确认它的 document 是谁的。



















