JS原型污染漏洞
2025-12-24 JS原型污染漏洞
原型污染(Prototype Pollution)是 JavaScript 里一类隐蔽但危害很大的漏洞,处理不可信输入时尤其容易中招。被利用后可能导致权限提升、任意代码执行、拒绝服务(DoS)等后果。
原型污染的基本原理
JavaScript 中几乎所有对象都继承自 Object.prototype,而原型可以通过 __proto__、prototype、constructor 这几个特殊属性访问和修改。
如果往一个普通对象上动态添加属性时,键名恰好是 __proto__,改动就会落到原型对象上,整条原型链随之被污染。
简单演示
// 正常情况:给空对象添加属性
const user = {};
user.name = 'Alice';
console.log(user.name); // 'Alice'
// 原型污染攻击
const maliciousInput = JSON.parse('{"__proto__": {"isAdmin": true}}');
// 假设有一个不安全的深层合并函数(常见于许多库)
function merge(target, source) {
for (const key in source) {
target[key] = source[key];
}
return target;
}
const cleanObj = {};
merge(cleanObj, maliciousInput);
// 现在所有普通对象的原型都被污染了!
console.log({}.isAdmin); // true ← 危险!任意空对象都有 isAdmin 属性
console.log(user.isAdmin); // true
console.log((new Date()).isAdmin); // true,甚至原生对象也可能受影响
攻击者只要让输入对象里出现 __proto__ 这样的键,就可能往 Object.prototype 上注入属性,之后创建的所有对象都会受影响。
常见的特殊键
以下键都会影响原型链(统称为污染入口):
__proto__← 最常见、最直接prototype← 常用于污染构造函数的原型constructor← 可间接访问 prototype
// 三种等效的污染方式
obj['__proto__'].polluted = true;
obj.constructor.prototype.polluted = true;
obj['constructor']['prototype'].polluted = true;
// 结果都是:Object.prototype.polluted === true
真实攻击场景举例
-
权限绕过
// 服务端校验用户权限 function isAdmin(user) { return user.isAdmin === true; // 本应从数据库读取 } // 攻击者提交 { "__proto__": { "isAdmin": true } } // 导致所有对象默认都有 isAdmin: true → 权限绕过成功 -
拒绝服务(DoS)
// 注入一个会导致无限循环或崩溃的 getter {"__proto__": {"toString": () => { while(true) {} }}} -
结合其他漏洞实现 RCE(远程代码执行) 在某些 Node.js 环境中,污染
child_process或fs相关原型后,可能配合其他 gadget 实现任意命令执行。历史上 lodash(< 4.17.11)等多个知名库都中过招。
如何防御原型污染
-
避免直接使用用户输入作为对象键进行递归合并
- 不要自己写深层 merge 函数
- 使用安全的库或手动控制合并逻辑
-
严格校验键名
function safeAssign(target, key, value) { if (key === '__proto__' || key === 'prototype' || key === 'constructor') { throw new Error('Forbidden property name'); } target[key] = value; } -
使用 Object.create(null) 创建无原型对象
const map = Object.create(null); // 没有 __proto__ 属性,完全安全 map[userInputKey] = value; // 即使 key 是 "__proto__" 也只是普通键 -
冻结原型(生产环境可考虑)
Object.freeze(Object.prototype); // 尝试污染会静默失败(非严格模式)或抛错(严格模式) -
升级依赖库 许多知名库(如 lodash、jquery、express 等)都修复过原型污染漏洞,保持依赖最新非常重要。
小结
原型污染的本质,是攻击者通过控制键名改掉了所有对象共享的原型。
它听起来冷门,实际上离日常开发很近,这几类场景要特别当心:
- 接收 JSON 输入的 API
- 做深层对象合并的配置系统
- 停留在旧版本、未修复漏洞的第三方库
防御原则一句话:外部输入不能直接拿来当对象属性键,递归合并时尤其如此。
评论讨论