IIFE沙箱通过函数作用域隔离与闭包机制实现轻量级代码隔离,关键在于传入全新安全上下文、显式控制API暴露、禁用eval/Function等逃逸通道,并配合Web Worker超时限制等运行约束。

做不到“绝对安全”,但能用带作用域隔离的函数表达式(IIFE)构建高可控、低逃逸风险的轻量沙箱。关键不是追求理论上的绝对,而是切断常见逃逸路径、显式控制暴露面、配合运行约束。
用IIFE封住作用域入口和出口
IIFE本身不提供安全,它只是隔离的起点。真正起作用的是:内部不引用任何外部变量,也不把原始对象直接挂进上下文。
- 写成
(function (context) { /* 沙箱主体 */ })(safeContext),而不是(function () { ... })()空参数——后者容易偷偷捕获外部闭包 - 传入的
context必须是全新构造的对象,不能是window、JSON或Array.prototype的引用,否则可能通过{}.constructor.constructor(...)逃逸 - 返回值只暴露明确设计的 API,比如
{ run, log, getStats },其余一切变量(包括let cache = new Map())对外不可见、不可探测
代理全局行为,不代理全局对象
别把 window 包一层 Proxy 就完事。应该只提供功能接口,而不是模拟全局环境。
- 需要 DOM 查询?暴露
query(selector),内部调用context.el?.querySelector(selector),不给document本身 - 需要延时?提供
delay(ms),返回封装过的 Promise,内部用setTimeout但限制最大时长(如 2000ms),不暴露原生setTimeout - 需要日志?只允许
console.log,且重写为收集到内部数组,禁止console.error或console.dir等可能泄露原型链的方法
堵死执行逃逸的三类通道
很多沙箱被绕过,不是因为 IIFE 写得不好,而是没管住这三条路:
-
禁用动态代码生成:删掉或重定义
eval、Function、setTimeout(字符串形式)、setInterval(字符串形式)。哪怕只留一个,就可能执行任意代码 -
切断原型链访问:在传入的
context上,冻结Object.prototype,并用 Proxy 拦截__proto__、constructor、prototype等敏感属性读取 -
限制构造能力:不暴露
new可用的原生构造器(如RegExp、Date、Promise),若必须支持,只提供封装后的工厂函数,如createDate(str),内部校验输入格式
加一层运行约束才真正落地
再干净的闭包,跑个 while(true){} 也会卡死线程。IIFE 沙箱必须搭配运行期防护:
- 浏览器环境用
Web Worker执行 IIFE 沙箱逻辑,主线程仅收发消息,天然规避 DOM 和 cookie 访问 - 设置硬性超时:Worker 中用
self.setTimeout(() => self.close(), 5000);Node 环境用vm2替代原生vm,启用超时与内存限制 - 所有异步操作统一走沙箱内事件总线,不触发原生
EventTarget,避免监听message或error事件反向渗透

















