防范 object.setprototypeof 绕过沙箱的关键是切断污染路径、封死继承面、使篡改无效或立即暴露:一、禁用不可信数据驱动的调用,清洗 \_\_proto\_\_ 等敏感键;二、优先使用 object.create(null) 替代继承;三、冻结 object.prototype 并对关键对象设原型为 null;四、运行时校验 setprototypeof 调用并监控原型链异常。

防范攻击者利用 Object.setPrototypeOf 绕过沙箱,关键不是“禁止调用”,而是切断污染路径、封死继承面、让篡改无效或立即暴露。
一、禁用不可信数据驱动的 setPrototypeOf 调用
所有来自用户输入、网络响应、URL 参数、localStorage 的数据,都不得作为 setPrototypeOf 的第二个参数(即目标原型)。这类调用等于主动交出控制权。
- JSON 解析后第一件事:递归扫描并删除所有含
__proto__、constructor、prototype的键(包括嵌套对象和点号路径) - 服务端接收请求体时,用正则
/\b(__proto__|constructor|prototype)\b/i扫描全部 key 和字符串路径 - 避免使用
lodash.set或_.merge处理不可信数据;若必须用,确保 lodash ≥ 4.17.21 并启用安全模式
二、用无原型对象替代继承式设计
多数所谓“需要换原型”的场景,本质只是想要一个干净的键值容器。此时 Object.create(null) 比任何 setPrototypeOf 都更安全、更轻量。
- 缓存映射、插件注册表、路由配置、表单字段集合——这些都不需要
toString或hasOwnProperty,直接用Object.create(null) - 判断属性是否存在?写
Object.prototype.hasOwnProperty.call(obj, key)即可,不依赖原型链 - 这样从源头消除
obj.__proto__可被覆盖或误读的风险
三、运行时冻结基础原型 + 切断继承链
在可信代码加载完毕后,立即冻结核心原型,并对敏感对象主动切断原型链。
- 执行
Object.freeze(Object.prototype),阻止后续任何对基础原型的新增/修改 - 对沙箱内关键对象(如上下文环境、权限服务实例),在初始化后调用
Object.setPrototypeOf(obj, null),使其不再继承Object.prototype - 冻结后,任何试图修改
Object.prototype的操作都会抛错,而非静默失败
四、沙箱内强制校验与行为监控
不能只靠“冻住”就高枕无忧,需在关键节点加入运行时防护:
- 每次调用
setPrototypeOf前,检查目标对象是否可扩展:Object.isExtensible(obj),且新原型不等于对象自身,也不在其现有原型链中(防循环) - 对高频操作(如策略对象切换、插件注入)做快照比对:记录初始原型链长度与构造器名,异常变化即告警
- 开发环境启用轻量扫描:启动时遍历关键对象原型链,发现重复或非标准构造器立即提示











