You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何移除JavaScript默认对象与函数 按需启用特性构建安全沙箱

默认全禁用、按需启用的JS精简沙箱落地实现方案

你现在直接在当前上下文重写globalThis、覆盖eval的做法存在本质安全漏洞,JS的原型链、作用域链存在大量隐式引用路径,只要漏堵一个点,用户就能通过类似({}).constructor.constructor的方式拿到原生构造函数,绕过你所有的禁用逻辑,根本做不到完全的默认禁用。

核心思路

放弃在主运行上下文里手动删/覆盖原生特性的黑名单思路,直接创建完全独立的隔离执行上下文,初始状态下上下文不携带任何JS原生内置对象与宿主API,后续只手动往上下文里注入你明确要开放的能力,从根源实现默认全禁用、白名单按需开启的效果。

具体实现步骤

1. 选择合适的隔离边界,创建空白初始上下文

根据你的运行环境选对应的隔离方案,不要在主上下文做清理:

  • Node.js环境:直接用内置的vm模块创建隔离上下文,初始全局用无原型的空对象,从根源避免原型链污染
    示例代码:
const vm = require('vm');
// 初始化完全空白的全局对象,无原型链,无任何默认属性
const sandboxGlobal = Object.create(null);
// 全局指向自身,避免访问到外部全局
sandboxGlobal.globalThis = sandboxGlobal;
// 👉 这里只手动添加你确定要开放的能力,比如自定义日志、固定的数学常量
sandboxGlobal.log = (...args) => {
  // 可以在这里加日志审计、内容过滤逻辑
  console.log('[沙箱输出]', ...args);
};
sandboxGlobal.Math = {
  PI: Math.PI,
  floor: Math.floor
};
// 创建隔离上下文,从引擎层面关闭高风险代码生成能力
const context = vm.createContext(sandboxGlobal, {
  codeGeneration: {
    strings: false, // 直接禁用eval、Function构造函数等从字符串生成代码的能力,不需要手动重写
    wasm: false // 直接禁用WebAssembly编译,堵死Wasm逃逸路径
  }
});
// 执行用户代码,加超时防止死循环
try {
  vm.runInContext(`
    // 用户代码区域
    log(Math.floor(Math.PI)); // 正常输出3
    // 访问eval、Reflect、WebAssembly、Object、Array全都是undefined
  `, context, {
    timeout: 1000, // 单段脚本执行超时时间1秒
    displayErrors: true
  });
} catch (err) {
  console.error('沙箱执行异常:', err.message);
}
  • 浏览器环境:优先用标准的ShadowRealmAPI创建独立域,兼容低版本浏览器可以用加了sandbox属性的iframe做隔离
    ShadowRealm示例代码:
// 创建独立隔离域
const realm = new ShadowRealm();
// 清空域内默认挂载的所有原生全局属性
realm.evaluate(`
  const keepKeys = new Set(['globalThis']);
  for (const key of Object.getOwnPropertyNames(globalThis)) {
    if (!keepKeys.has(key)) {
      delete globalThis[key];
    }
  }
`);
// 手动注入需要开放的能力
realm.evaluate(`
  globalThis.log = (text) => console.log('[沙箱]', text);
`);
// 执行用户代码
realm.evaluate(`
  log('测试执行');
  // console.log(eval, Reflect, WebAssembly) // 全为undefined
`);

如果用iframe方案,必须给iframe标签加sandbox="allow-scripts"属性,不要加allow-same-origin、allow-top-navigation等其他权限,保证iframe和主页面完全跨源隔离,加载完成后清空iframe contentWindow上的所有默认属性,再注入白名单能力即可。

2. 严格执行白名单规则,不要直接透传原生引用

所有注入沙箱的能力都要遵守几个原则:

  • 不要直接把原生对象、原生函数透传到沙箱里,比如要开放数组能力,不要直接把全局Array传进去,自己封装精简的实现,只暴露你需要的方法,剔除constructor、__proto__这类可以沿原型链逃逸的属性。
  • 所有传入沙箱的回调函数,都要做参数校验、执行超时控制,避免通过回调逻辑拿到外部上下文引用。
  • 除非明确需要,否则不要开放Object、Reflect上的原型操作相关方法,比如getPrototypeOf、defineProperty、getOwnPropertyDescriptor这类API,这类API是沙箱逃逸最常用的入口。

3. 补全运行时防护

  • 所有沙箱代码必须加执行超时限制,防止用户写死循环阻塞主进程,Node.js环境建议把沙箱跑在独立的worker_threads里,超时直接终止线程,浏览器环境建议把沙箱跑在Web Worker里,避免阻塞主线程。
  • 加内存占用限制,防止用户写恶意代码占满内存导致服务崩溃。
  • 不要给沙箱开放任何宿主环境的敏感对象,比如Node.js里的process、require,浏览器里的document、window、fetch这类API,如果要开放必须自己做一层严格的权限校验包装。

必须避开的坑

  • 不要用黑名单思路做防护:你永远列不全所有高风险API,JS标准迭代会持续新增新的API,黑名单永远有漏网之鱼,白名单默认全关的模式才是长期可靠的。
  • 不要在主上下文里做清理:主上下文的原生引用隐式路径极多,靠手动覆盖、删除属性永远清不干净,非常容易被逃逸。
  • 不要依赖重写高危函数的方式做防护:比如把eval重写为空函数,这类重写可以通过原型链拿到原生eval引用绕过,直接从引擎层面关闭代码生成能力才是可靠的。

内容的提问来源于stack exchange,提问作者Nightfall

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 23:42:19