深度克隆本身不天然安全,关键看实现方式;若盲目遍历并设置__proto__、constructor等敏感属性,会污染原型链引发原型污染攻击,安全方案需拦截敏感路径、使用Object.create(null)或structuredClone()等。

深度克隆本身不天然安全,关键看实现方式。如果克隆过程盲目遍历并设置所有属性(包括 __proto__、constructor、prototype),就会把恶意键名原样复制到新对象上,进而污染其原型链——这就是原型污染攻击的典型入口。
为什么普通深拷贝容易触发原型污染
很多手写或旧版工具(如 lodash target[key] = clonedValue 赋值。当遇到 {"__proto__": {"isAdmin": true}} 这类输入时,target.__proto__ 就会被赋值,实际修改了 target 的隐式原型,而该原型又继承自 Object.prototype,导致后续所有普通对象都可能带上 isAdmin: true。
- JSON.parse() 后直接深拷贝,等于二次执行危险操作
- 只过滤顶层
__proto__不够,攻击者可用"a.b.__proto__.c"绕过 - 使用
Object.assign({}, obj)浅拷贝也危险——它虽不递归,但若obj已被污染,目标对象仍会继承污染后的原型
安全深度克隆的核心防护点
真正安全的克隆必须在属性写入前主动拦截敏感路径,并切断原型继承链条:
- 递归过程中对每个 key 路径做正则扫描,例如
/\b(__proto__|constructor|prototype)\b/i,匹配即跳过 - 对
__proto__特殊处理:不用简单赋值,改用Object.defineProperty(target, '__proto__', {...})控制行为 - 优先创建无原型对象作为克隆容器:
Object.create(null),从源头移除污染载体 - 避免依赖用户可控的 key 名动态赋值;必要时用白名单限定允许写入的字段
生产环境推荐的安全方案
不必自己重造轮子,优先选用经过安全验证的现成方案:
-
klona/src/json.js:仅 28 行,专为 JSON 安全类型设计,显式防御
__proto__,适合配置类数据 -
destr:解析 + 克隆一体化,默认过滤原型污染键,比
JSON.parse更稳妥 - structuredClone()(现代浏览器/Node.js 17+):原生 API,不走原型链,自动忽略不可序列化属性和敏感键
- jsondiffpatch.clone():专为差异对比场景优化,内置深度克隆保护机制
额外加固建议
克隆只是防线一环,还需配合其他措施形成纵深防御:
- 接收 JSON 前先校验大小,防爆破式嵌套污染(如
JSON.stringify(data).length > 1024 * 1024拒绝) - 服务端路由层启用 JSON Schema 验证,从结构上拒绝含敏感字段的请求体
- 对配置类对象、插件注册表等关键字典,统一用
Object.create(null)初始化 - 禁用
eval、Function构造器,避免污染后执行任意代码

















