json解析后必须立即递归清洗敏感键,遍历所有层级键名及路径,用正则匹配__proto__、constructor、prototype并删除对应子树;存储对象应使用object.create(null);禁用_.merge等高危操作;启动时冻结object.prototype并增加运行时验证。

直接拦截在 JSON 解析那一刻——所有外部 JSON 字符串调用 JSON.parse() 后,必须立即递归清洗,不能等合并、赋值或业务逻辑再处理。这是防线的第一道闸口,晚一步就可能全局污染。
解析后立刻递归清洗敏感键
攻击者常通过嵌套结构绕过表层检查,比如 {"a": {"__proto__": {"x": 1}}} 或带路径的 {"x.__proto__.y": "z"}。清洗不能只看顶层 key,要遍历所有层级的键名和完整路径字符串:
- 用正则
/\b(__proto__|constructor|prototype)\b/i匹配每个键名,也匹配点号分隔的路径(如a.__proto__.b) - 对
JSON.parse()返回的对象执行深度遍历,遇到匹配项直接删除该键及其整个子树 - 避免写
if (key === '__proto__') continue这类字面量判断,它对大小写变形(如__PROTO__)或编码绕过完全无效
用 Object.create(null) 替代 {} 创建容器
很多污染扩散,是因为把用户数据合并进了普通对象 {}。它天然继承 Object.prototype,一旦原型被改,所有后续创建的对象都受影响。凡用于存储映射、参数、配置的“字典型”对象,应主动弃用 {}:
详细的 Three.js 3D 图形参考,涵盖场景设置、相机、几何体、材质、光照、动画、控制器、加载器、数学工具和调试。
- 声明时统一写
const params = Object.create(null) - 判断键是否存在,改用
Object.prototype.hasOwnProperty.call(params, key) - 注意
params.toString会报错,但这正是你想要的——无原型即无污染入口
禁用高危合并与动态赋值操作
一个 _.merge({}, userInput) 或 obj[path] = value(其中 path 来自 URL)就可能触发污染。这类操作本质是把用户输入当作路径指令来执行:
- 浅拷贝优先用
Object.assign({}, input)—— 它不递归,跳过所有嵌套污染路径 - 必须深拷贝时,现代环境用
structuredClone();Node.js 可加启动参数--disable-proto=throw - 若依赖 Lodash,版本必须 ≥ 4.17.21;旧版
_.merge默认允许写入__proto__,且存在已知绕过方式
运行时冻结 + 主动验证兜底
静态防护可能被新型变体绕过,需加一层动态保障:
- 服务启动最开始(第三方脚本加载前),执行
Object.freeze(Object.prototype)。之后任何篡改都会抛TypeError,而非静默生效 - 预发环境定期注入测试 payload:
{"__proto__": {"polluted": true}},随后检查({ }).polluted === undefined - 在
req.body、JSON.parse入口处加日志扫描,命中敏感键立即告警并记录上下文










