JSON.parse()本身安全,危险在于解析后直接合并或赋值导致原型污染;需过滤__proto__、constructor、prototype等敏感键及其变体,推荐递归清理而非依赖reviver。

JSON.parse() 本身不安全,但问题不在它身上
JSON.parse() 是中立的——它只还原结构,不执行代码。真正危险的是后续把解析结果直接用于对象合并、递归赋值或原型链操作。比如收到 RabbitMQ 消息 {"__proto__": {"isAdmin": true}},若直接 Object.assign({}, parsed) 或传给 lodash.merge,就会污染 Object.prototype。
必须在解析后立即过滤敏感键名
不能依赖 reviver 参数做最终防护,因为 reviver 对顶层 __proto__ 键无效(它被当作普通属性处理,不触发原型写入拦截)。正确做法是解析后手动遍历并删除危险键:
- 需过滤的键包括:
"__proto__"、"constructor"、"prototype",及其大小写变体(如"__PROTO__") - 推荐用递归函数清理嵌套对象,但注意:只删键,不删值;避免修改原始消息用于审计追踪
- 示例清理逻辑:
function sanitize(obj) { if (obj !== null && typeof obj === 'object') { Object.keys(obj).forEach(key => { if (key === '__proto__' || key === 'constructor' || key === 'prototype') { delete obj[key]; } else if (typeof obj[key] === 'object') { sanitize(obj[key]); } }); } return obj; }
避免使用存在风险的深拷贝/合并工具
像 lodash.merge(hoek.merge(__proto__。Node.js 环境下更稳妥的选择是:
- 浅拷贝优先用
Object.assign({}, input)或展开运算符{...input},它们不会遍历原型链 - 真需要深拷贝时,现代环境用
structuredClone(input)(Node.js ≥17.0,默认禁用原型键) - 若需兼容旧版 Node.js,自定义深克隆函数,并显式跳过上述敏感键名,不调用
JSON.parse(JSON.stringify())(会丢失函数、undefined、Date 等)
RabbitMQ 消费端要设防的三个关键点
消息体来自不可信来源,防御必须贯穿整个消费流程:
- 消费回调里第一行就做
try/catch包裹JSON.parse(),防止非法 JSON 导致进程崩溃 - 解析后立刻调用
sanitize()清理,**不要等到业务逻辑中间再处理** - 禁止将清洗后的对象直接作为配置项、路由参数或模板上下文传入高危函数(如
eval、Function、res.render),这些场景可能触发 SSTI 或原型链二次利用
最易被忽略的是:你以为只用了 JSON.parse() 就安全了,但只要后续有任意一步做了递归赋值或合并,污染就可能发生。防护不是加一道锁,而是切断所有通往 Object.prototype 的写入路径。


















