防范原型污染攻击的关键在于切断污染入口、物理隔离执行环境、冻结不可信行为路径:启动即冻结Object.prototype等核心原型,禁用Object.setPrototypeOf等危险API,使用Object.create(null)构建无原型对象,并结合进程级隔离与运行时监控。
防范攻击者利用 Object.setPrototypeOf 恶意篡改内置对象(如 Object.prototype、Function.prototype)来绕过沙箱的文件读写控制,关键在于**切断污染入口、物理隔离执行环境、冻结不可信行为路径**。这类攻击常见于 Node.js 沙箱(如 vm 模块)或浏览器中嵌入第三方 SDK 的场景,一旦原型被污染,后续所有对象都会继承恶意方法(例如伪造 fs.readFile 返回值、劫持 require 加载逻辑),导致沙箱形同虚设。
在沙箱初始化完成、任何用户脚本执行前,立即冻结所有基础原型:
Object.freeze(Object.prototype)、Object.freeze(Function.prototype)、Object.freeze(Array.prototype) —— 这能阻止新增/修改/删除属性,包括 __proto__ 和 constructor
--disable-proto=throw,让任何对 __proto__ 的赋值直接抛错,而非静默失败仅靠冻结不够,还需主动封禁可被滥用的原生能力:
Object.setPrototypeOf 替换为无操作函数:Object.setPrototypeOf = () => { throw new Error('setPrototypeOf disabled in sandbox'); };
Object.__proto__ 的 setter(如果环境支持):Object.defineProperty(Object.prototype, '__proto__', { set: () => { throw new Error('__proto__ assignment blocked'); } });
Function.prototype.bind、Reflect.setPrototypeOf 等间接入口也做同样处理,避免绕过沙箱内部用于通信、配置、权限校验的对象,一律不用 {},改用更干净的基底:
const permissions = Object.create(null); permissions['fs.readFile'] = true;
Object.prototype.hasOwnProperty.call(permissions, key) 判断是否允许调用,避免因原型污染导致误判Object.create(null),从源头消除原型链查找依赖对于高敏感场景(如云函数、低代码平台),需超越 JS 层防护:
vm2 而非原生 vm,并启用其 timeout 和 memoryLimit 限制,防止长时间运行绕过检测seccomp-bpf 或容器级 syscall 过滤,禁止 openat、read 等系统调用,即使原型被污染也无法真正触发文件操作if ({}.toString !== Object.prototype.toString) throw new Error('Prototype polluted');,发现异常立刻终止上下文不复杂但容易忽略——真正的防护不是等攻击发生再打补丁,而是在沙箱诞生那一刻,就让它运行在一个“没有原型可被污染”的干净世界里。