防范原型污染需在输入解析、对象操作、运行时环境三环节同步设防:输入层用正则清洗敏感键;对象操作避开高危函数,优先用object.assign或structuredclone;用object.create(null)替代{};运行时增加监控与冻结防护。
防范原型污染,关键在于阻断用户输入向 object.prototype 写入的路径。它不是一次配置就能解决的问题,而是需要在输入解析、对象操作、运行时环境三个环节同步设防。
输入层必须清洗敏感键名
所有外部数据(JSON 请求体、URL 查询参数、表单字段)进入业务逻辑前,都要递归扫描并移除含 __proto__、constructor、prototype 的键或嵌套路径。
- 不能只检查顶层键,例如
{"a": {"__proto__": {"x": 1}}}或{"x.__proto__.y": "z"}都需匹配 - 推荐正则:
/\b(__proto__|constructor|prototype)\b/i,对每个 key 和完整路径字符串做检测 - JSON.parse 后第一行代码就该是清洗,而不是等到合并或赋值时才处理
对象操作要避开高危函数
很多深拷贝和合并操作默认不校验原型键,尤其在处理不可信输入时极易触发污染。
- 避免使用
_.merge、_.set、_.defaultsDeep处理用户数据;若必须用,确保 lodash ≥ 4.17.21 - 优先改用
Object.assign({}, input)做浅拷贝——它不递归,天然跳过污染路径 - 需要深拷贝时,现代环境用
structuredClone();Node.js 可启用--disable-proto=throw参数强制拦截 - 手动实现递归逻辑时,遇到敏感键名直接跳过该分支,不进入下一层递归
用无原型对象替代普通空对象
当只需要一个纯键值容器(如缓存、路由参数映射、插件注册表),就别再用 {}。
- 改用
Object.create(null)创建对象,它没有__proto__,也不继承任何方法 - 代价是不能直接调用
obj.toString()或obj.hasOwnProperty(),需显式借用:Object.prototype.hasOwnProperty.call(obj, key) - 适合所有你本就不希望它有“行为”的场景,从根源上切断污染可能
运行时加一层兜底防护
静态防御可能被绕过,上线后仍需动态监控与快速响应。
- 预发环境定期注入测试 payload,例如
{"__proto__": {"polluted": true}},验证({}).polluted === true是否成立 - 在日志中对
JSON.parse、req.body、fs.readFileSync等入口点添加字段扫描,命中敏感键立即告警 - 高安全要求场景可启动时执行
Object.freeze(Object.prototype),但注意兼容性,部分老库会因此报错










