关键不是“避免污染”,而是从源头切断污染路径——必须阻止用户输入写入object.prototype。首选object.create(null)替代{}创建无原型对象,配合输入层递归清洗敏感键(如__proto__)、禁用高危合并函数、运行时冻结object.prototype兜底。

关键不是“避免污染”,而是从源头切断污染路径——父类实例属性本身不会被污染,真正危险的是用户输入意外写入 Object.prototype,导致所有对象(包括父类实例)继承恶意属性。所以重点是:不让外部数据触达原型链。
用 Object.create(null) 替代普通对象字面量
凡用于接收、缓存或中转用户数据的容器(如配置对象、路由参数、插件注册表),一律不用 {}:
- 改用
Object.create(null)创建无原型对象,它没有__proto__,也不继承toString、hasOwnProperty等方法 - 需要调用原生方法时,显式借用:
Object.prototype.hasOwnProperty.call(obj, key) - 适用场景明确:请求体解析结果、JSON 配置加载后暂存、动态生成的映射表
所有输入入口强制过滤敏感键名
不能依赖“后面再处理”,必须在数据进入业务逻辑前就清洗:
- 对 JSON 解析后的对象递归扫描每个键名和嵌套路径,正则匹配:
/\b(__proto__|constructor|prototype)\b/i - 特别注意深层形式,如
{"a": {"__proto__": {"x": 1}}}或{"x.__proto__.y": "z"} - 清洗动作放在
JSON.parse()后第一行,而不是合并或赋值环节
避开高危对象操作函数
很多工具默认不校验原型键,极易成为污染入口:
- 禁用
lodash.merge、_.set、_.defaultsDeep处理不可信输入;若必须用,确保 lodash ≥ 4.17.21 - 浅拷贝优先用
Object.assign({}, input),它不递归,天然跳过污染路径 - 需深拷贝时,现代环境首选
structuredClone();Node.js 可启用--disable-proto=throw
运行时加固作为兜底手段
静态防御可能被绕过,高安全要求场景建议加一层防护:
- 服务启动时执行
Object.freeze(Object.prototype),阻止新增/修改属性(注意:不可逆,需确认无合法依赖) - 预发环境注入测试 payload(如
{"__proto__": {"polluted": true}}),验证是否生效 - 在日志中对关键入口(如
req.body、fs.readFileSync)添加敏感键扫描与告警











