在前端领域,JS 沙箱(JavaScript Sandbox) 是一种安全机制,用于隔离和限制代码的执行环境,防止不受信任的脚本访问或修改外部资源(如全局变量、DOM、本地存储等),从而避免安全风险或意外冲突。
核心作用
JS 沙箱的核心是创建一个「隔离环境」,让内部代码只能在有限范围内运行,主要解决:
- 全局变量污染:防止沙箱内的变量 / 函数覆盖外部全局对象(如
window); - 权限控制:限制沙箱内代码对敏感 API 的访问(如
localStorage、fetch); - 环境隔离:在同一页面中安全运行多个独立脚本(如微应用、第三方插件)。
常见实现方式
根据隔离强度和使用场景,JS 沙箱有多种实现方案:
1. with + 代理对象(Proxy)
通过 with 语法改变作用域链,并结合 Proxy 拦截对象访问,是轻量级沙箱的常用方式。
javascript
运行
function createSandbox() {
// 沙箱内的全局对象(模拟 window)
const sandboxGlobal = new Proxy({}, {
get(target, key) {
// 优先从沙箱内获取,不存在则从外部 window 获取
return key in target ? target[key] : window[key];
},
set(target, key, value) {
// 只允许修改沙箱内的对象,不污染外部 window
target[key] = value;
return true;
}
});
// 执行沙箱内代码
function runCode(code) {
with (sandboxGlobal) {
// 使用 eval 执行代码,此时作用域为 sandboxGlobal
eval(code);
}
}
return { runCode, sandboxGlobal };
}
// 使用沙箱
const sandbox = createSandbox();
sandbox.runCode(`
a = 1; // 存储在 sandboxGlobal 中,不影响外部 window.a
console.log(b); // 若外部 window.b 存在则输出,否则 undefined
`);
console.log(window.a); // undefined(未被污染)
console.log(sandbox.sandboxGlobal.a); // 1(沙箱内变量)
缺点:with 语法性能较差,且无法完全阻止对外部全局对象的访问(如通过 window.xxx 直接访问)。
2. iframe 沙箱
利用浏览器原生的 iframe 标签创建独立的全局环境,隔离性最强。
html
预览
<!-- 主页面 -->
<iframe id="sandboxFrame" sandbox="allow-scripts"></iframe>
<script>
const iframe = document.getElementById('sandboxFrame');
const iframeWindow = iframe.contentWindow;
// 向 iframe 注入代码并执行
function runInIframe(code) {
const script = iframeWindow.document.createElement('script');
script.textContent = code;
iframeWindow.document.body.appendChild(script);
}
// 执行不受信任的代码
runInIframe(`
a = 1; // 仅在 iframe 的 window 中有效
console.log(window.a); // 1(iframe 内)
// 受 sandbox 属性限制,无法访问父页面或本地存储
`);
console.log(window.a); // undefined(主页面不受影响)
</script>
特点:
- 完全独立的全局环境,隔离性最好;
- 通过
sandbox属性可精细控制权限(如allow-scripts允许执行脚本,allow-same-origin允许同源访问等); - 缺点是通信成本高(需通过
postMessage),性能开销较大。
3. 快照沙箱(Snapshot Sandbox)
适用于单线程环境(如微前端),通过记录全局对象的「快照」,在沙箱激活 / 销毁时恢复状态。
javascript
运行
class SnapshotSandbox {
constructor() {
this.snapshot = new Map(); // 存储全局对象初始状态
this.modified = new Map(); // 存储沙箱内修改的属性
this.active = false;
}
// 激活沙箱:记录当前全局状态快照
activate() {
this.snapshot.clear();
// 记录 window 上的关键属性(如 document、location 等)
['a', 'b', 'c'].forEach(key => {
this.snapshot.set(key, window[key]);
});
// 恢复之前修改的属性
this.modified.forEach((value, key) => {
window[key] = value;
});
this.active = true;
}
// 销毁沙箱:恢复全局状态到初始快照
deactivate() {
this.modified.clear();
// 记录沙箱内修改的属性,并恢复初始值
['a', 'b', 'c'].forEach(key => {
if (window[key] !== this.snapshot.get(key)) {
this.modified.set(key, window[key]);
window[key] = this.snapshot.get(key); // 恢复快照值
}
});
this.active = false;
}
}
// 使用示例
const sandbox = new SnapshotSandbox();
sandbox.activate();
window.a = 1; // 在沙箱激活时修改
sandbox.deactivate();
console.log(window.a); // 恢复为初始值(如 undefined)
适用场景:微前端中切换子应用时,隔离不同应用对全局变量的修改(如 qiankun 框架早期使用此方案)。
4. VM 模块(Node.js 环境)
在 Node.js 中,可通过内置的 vm 模块创建沙箱,隔离代码执行环境:
javascript
运行
const vm = require('vm');
// 创建沙箱上下文
const sandbox = { x: 1, y: 2 };
const context = new vm.createContext(sandbox);
// 在沙箱中执行代码
const code = 'x + y;';
const result = vm.runInContext(code, context);
console.log(result); // 3(沙箱内计算结果)
console.log(sandbox.x); // 1(沙箱内变量未被污染)
典型应用场景
- 微前端框架:如
@micro-zoe/micro-app、qiankun等,通过沙箱隔离各子应用的全局变量和样式,避免冲突; - 代码在线编辑器:如 CodeSandbox、JSFiddle,使用沙箱安全执行用户输入的代码;
- 第三方插件 / 广告:在页面中嵌入第三方脚本时,通过沙箱限制其权限,防止恶意行为;
- 测试环境:隔离测试用例,避免测试代码影响全局环境。
总结
JS 沙箱的核心是「隔离」与「限制」,不同实现方案在隔离强度、性能和易用性上各有取舍:
- 轻量场景(如微应用)可选择
Proxy或快照沙箱; - 高安全需求(如执行未知代码)优先使用
iframe或 Node.js 的vm模块。
实际开发中需根据具体场景选择合适的方案,平衡安全性和性能。