原型链污染是JavaScript中因不当处理对象属性导致的高危安全漏洞,攻击者通过控制输入向Object.prototype写入恶意属性,影响所有对象行为;其本质是未过滤用户可控键名(如__proto__、constructor)的递归赋值或深拷贝操作。

原型链污染本身不是内存泄漏的直接原因,它属于安全漏洞范畴,但确实可能间接引发内存持续增长——关键在于被污染的原型上意外挂载了大量数据或监听器,而这些内容因原型的全局性难以被释放。
原型链污染如何导致内存异常增长
当 Object.prototype 或其他内置构造函数原型被篡改(如添加了 cache、handlers、data 等属性),所有继承该原型的对象实例都会共享这些新增属性。如果代码中频繁创建对象(如 { }、new Date()、[].map() 返回的新数组等),它们都会隐式携带对污染属性的引用。一旦这些属性是大型数组、闭包函数或 DOM 节点缓存,就形成“一个污染点,千个对象共用并持续持有着”的局面。
例如:
- 攻击者或错误逻辑执行了
Object.prototype.cache = new Array(100000).fill({}),后续每个普通对象都可通过obj.cache访问该大数组; - 某个工具函数遍历对象属性时未过滤原型链,把 handler 函数反复 push 到
obj.__proto__.handlers,结果成千上万个对象实例共同引用同一份 handlers 数组; - 框架或插件在初始化时向
Function.prototype添加了__meta字段,而该字段包含对组件实例的强引用,卸载后无法回收。
用 Chrome DevTools 定位原型污染型内存增长
打开 Memory 面板,按以下顺序操作:
立即学习“Java免费学习笔记(深入)”;
- 刷新页面,拍第一张 Heap Snapshot(基线);
- 触发疑似污染的操作(如解析不受信 JSON、调用第三方 parse 方法、加载恶意配置);
- 再拍两张快照,切换到 Comparison 视图;
- 重点关注 Object、Array、Closure 和 system / Context 类型的增长;
- 筛选出新增量大的对象,点击展开其 Retaining Path,若路径中出现
Object.prototype、Array.prototype或Function.prototype,且下方有非标准属性名(如cache、handlers、__internal),基本可确认污染已发生并正在拖累内存。
验证与修复原型污染的关键检查点
在 Console 中运行这几条命令快速筛查:
-
Object.getOwnPropertyNames(Object.prototype)—— 查看是否多了不该有的属性; -
Object.getOwnPropertyNames(Array.prototype).filter(k => !['push','pop','map'].includes(k))—— 检查数组原型是否被注入; -
for (let k in {}){ if ({}.hasOwnProperty.call({}, k) === false) console.log('leaked key:', k) }—— 找出所有从原型链泄露出来的键; - 搜索项目中是否存在
Object.prototype[xxx] =、Object.setPrototypeOf、__proto__赋值等高危操作。
修复原则:禁用不安全的合并/赋值逻辑(如 Lodash 的 merge、defaultsDeep),使用 Object.assign({}, unsafeInput) 替代深拷贝,对不可信输入做 schema 校验和 key 白名单过滤。
为什么 WeakMap 不能解决原型污染问题
WeakMap 的键必须是对象,且只持有弱引用——但它无法防止原型本身被污染。即使你用 WeakMap 缓存数据,若污染发生在 Object.prototype 上,所有普通对象仍会通过原型链暴露污染字段,进而可能被误读、误写、误传,最终让 WeakMap 的键对象被意外长期持有(比如污染导致某个对象始终无法被 GC,它作为 WeakMap 键也就一直有效)。所以 WeakMap 是防泄漏的好工具,但不是防污染的盾牌。


















