本文的教学代码在 github.com/duheng1992/... ,欢迎大家批评指正,如果觉得好,还望给点个星星啦!😊
项目地址:monto-js-sandbox
本文系统梳理一下前端常见的 JavaScript 沙盒隔离技术,结合实战项目,带你深入理解各类沙盒的原理、优缺点与应用场景。
为什么需要 JS 沙盒?
在微前端、插件系统、在线编辑器、低代码平台等场景中,经常需要安全地运行第三方或不可信的 JavaScript 代码。沙盒(Sandbox)技术可以有效隔离运行环境,防止全局污染、数据泄露和恶意攻击。
Get started
npm install monto-js-sandbox
以下是一个基于 proxy 的简单沙箱实现示例,可用于安全地加载和执行第三方 JavaScript 代码:
js
import { ProxySandbox } from 'monto-js-sandbox';
const sb = new ProxySandbox({
rootElm: globalThis
});
const a = 333;
const scriptText = 'console.log("沙盒中的a: ", a);var a = 111;';
sb.execScript(scriptText);
console.log("父容器中的a: ", a)
sb.destroy();
1. Proxy 沙盒
技术原理
- 利用 Proxy代理空对象作为全局变量,并传递白名单里的真实root的属性
- 利用 with 语句来向代码块传递全局变量。
- 通过 has、get、set 等钩子,实现对全局对象的"虚拟化"。
- 可实现"伪私有"属性、只读沙盒等扩展。
伪代码
js
const sandbox = new Proxy({}, {
has(target, key) {
// 隐藏下划线开头的属性
if (key.startsWith('_')) return false;
return true;
},
get(target, key) {
// 白名单属性透传到真实 window
if (typeof prop === 'string' && whiteList.has(prop)) {
// 允许沙盒内访问 window、globalThis、self、top、parent 时返回代理自身,防止逃逸
if (['window', 'globalThis', 'self', 'top', 'parent'].includes(prop)) {
return receiver;
}
// 其他白名单属性透传
return realWindow[prop];
}
// 其余属性在 {} 上隔离
if (prop in target) {
return target[prop];
}
return undefined;
},
set(target, key, value) {
target[key] = value;
return true;
}
});
with (sandbox) {
// 用户代码
}
优缺点
- 优点:实现简单,性能好,灵活可扩展。
- 缺点:无法彻底隔离全局对象,不能防御 globalThis、Function、eval 等绕过手段,存在原型链污染风险。
应用场景
- 低风险的插件隔离
- 需要灵活扩展的沙盒场景
qiankun(以及 single-spa 微前端沙箱体系)采用的 JS 沙箱方案,本质上和 Proxy 沙盒原理类似,即通过 Proxy+with 劫持全局变量访问,但它也无法彻底防御 Function('globalThis.foo = 1') 这种"全局标识符静态解析"的环境污染。

2. iframe 沙盒
技术原理
- 利用
<iframe sandbox>
属性,浏览器原生实现强隔离。 - 可通过 postMessage 实现主页面与沙盒通信。
- 可配合 CSP、Referrer-Policy 等安全头部,进一步提升安全性。
架构示意

优缺点
- 优点:隔离性最强,几乎无沙盒逃逸风险。
- 缺点:性能开销大,通信复杂,无法直接操作主页面 DOM。
应用场景
- 一般用于跨域场景
- 运行完全不可信的第三方代码
- 在线 IDE、低代码平台 (比如 码上掘金)
3. 快照沙盒(SnapshotSandbox)
技术原理
- 激活时对全局对象做快照,执行后对比差异(diff)。
- 销毁时还原所有被污染的全局属性。
伪代码
js
class SnapshotSandbox {
activate() {
// 记录快照
this.recordOriginGlobal(this.$root);
}
execScript(code) {
eval(code);
// 记录 diff
recordDiffPropsMap();
}
deactivate() {
// 从 diff 还原root
recoverOriginGlobal();
}
}
优缺点
- 优点:能还原全局环境,适合单实例串行运行。
- 缺点:快照沙盒不是运行时隔离,而是"允许污染、事后还原",所以多实例并发时,作用域会被反复污染。
应用场景
- 微前端子应用串行加载 或者子应用之间共生加载
- 需要全局环境还原的场景

4. WebComponent 沙盒
技术原理
- 利用 WebComponent + Shadow DOM 实现 DOM/CSS 隔离。
- JS 作用域通过参数注入部分隔离,只暴露白名单变量。
伪代码
js
class WebComponentSandbox {
private avtive() {
// 注册自定义元素(只注册一次)
if (!customElements.get(tagName)) {
customElements.define(tagName, class extends HTMLElement {});
}
// 创建元素并挂载
this.element = document.createElement(tagName);
container.appendChild(this.element);
// 创建 Shadow DOM
this.shadowRoot = this.element.attachShadow({ mode: 'open' });
}
execScript() {
const fn = new Function('');
}
}
优缺点
- 优点:DOM/CSS 隔离彻底,灵活可控。
- 缺点:JS 作用域隔离有限,需谨慎注入变量。
应用场景
- 需要样式隔离的插件系统,往往和 proxy 结合使用
- 微前端 UI 组件隔离

5. QuickJS + WASM 沙盒
技术原理
- 基于 quickjs-emscripten 的 WASM 虚拟机,主环境与沙盒环境完全隔离。
- 通过 API 创建上下文、对象、函数,执行代码,手动管理内存。
代码片段
js
class QuickJSSandbox {
private async active() {
this.QuickJS = await getQuickJS();
this.vm = this.QuickJS.newContext();
// 桥接 console.log 到主环境
const vm = this.vm;
// const qjs = this.QuickJS;
// 在 QuickJS 沙盒环境中桥接(注入)主环境的 console.log 方法,让沙盒内的 JS 代码可以安全地调用 console.log,并把日志输出到主环境的控制台。
const consoleObj = vm.newObject();
const logFn = vm.newFunction('log', (...args: any[]) => {
// dump 可将 QuickJS 值转为主环境值
console.log('[沙盒]', ...args.map((arg) => vm.dump(arg)));
});
vm.setProp(consoleObj, 'log', logFn);
vm.setProp(vm.global, 'console', consoleObj);
// 这里应该加上,不然会内存泄漏
consoleObj.dispose();
logFn.dispose();
}
async execScript(scriptText: string): Promise<any> {
const result = this.vm.evalCode(scriptText);
}
}
优缺点
- 优点:隔离性最强,支持多实例并发,适合运行不可信 JS。
- 缺点:依赖 WASM,体积较大,API 使用门槛高。沙盒内 JS 不能直接访问 DOM、window、document 等浏览器原生对象,只能通过你暴露的接口与主页面通信。同时对于内存管理要求较高。
应用场景
- 在线 IDE、沙盒执行环境
- 后端安全 JS 执行、插件系统
项目亮点
- 多种沙盒方案一站式体验:Proxy、iframe、快照、WebComponent、QuickJS+WASM 全覆盖。
- 丰富的实战案例:每种沙盒均有独立示例,便于对比和学习。
- 安全性与性能兼顾:适合不同场景灵活选型。
- 完善的文档与架构图:助力快速上手与二次开发。
monto-js-sandbox 项目已开源,欢迎 Star、Fork、交流与贡献!