Node.js 原型污染攻击的分析与利用
字数 1283 2025-08-26 22:11:22
Node.js 原型污染攻击分析与防御指南
1. JavaScript原型基础
1.1 JavaScript对象模型
JavaScript中的对象是键值对的集合,每个键值对称为属性:
var obj = {
"name": "0daylabs",
"website": "blog.0daylabs.com"
};
即使我们只定义了name和website两个属性,对象实际上包含更多信息,这些额外信息来自原型链。
1.2 原型链机制
Object是所有对象的基类- 可以通过
Object.create(null)创建真正的空对象(无原型) - 每个对象都有
__proto__属性指向其构造函数的prototype
1.3 构造函数与原型
JavaScript中函数同时充当构造函数:
function Person(fullName, age) {
this.age = age;
this.fullName = fullName;
}
var person1 = new Person("Anirudh", 25);
关键点:
- 函数创建时自动获得
prototype属性 - 对象创建时自动获得
__proto__属性指向构造函数的prototype constructor属性指向创建该对象的函数
2. 原型污染原理
2.1 原型动态修改
可以在运行时修改原型属性:
Person.prototype.details = function() {
return this.fullName + " has age: " + this.age;
}
或者通过对象修改:
person1.constructor.prototype.details = function() {
return this.fullName + " has age: " + this.age;
}
2.2 污染攻击向量
当存在以下操作时可能发生原型污染:
- 对象递归合并
- 按路径定义属性
- 对象克隆
攻击模式:
obj[a][b] = value
如果攻击者控制a和value,可以将a设为__proto__,从而为所有对象定义属性b。
3. 实际攻击案例分析
3.1 漏洞代码示例
function merge(a, b) {
for (var attr in b) {
if (isObject(a[attr]) && isObject(b[attr])) {
merge(a[attr], b[attr]);
} else {
a[attr] = b[attr];
}
}
return a
}
function clone(a) {
return merge({}, a);
}
3.2 攻击过程
- 发送恶意JSON负载:
{"__proto__": {"admin": 1}}
- 通过
clone()或merge()处理时:
- 遍历属性发现
__proto__ - 递归处理
a[__proto__]和b[__proto__] - 实际上修改了
Object的原型,添加了admin属性
- 所有对象现在都具有
admin: 1属性
3.3 完整攻击链
curl -vv --header 'Content-type: application/json' -d '{"__proto__": {"admin": 1}}' 'http://target/signup'
curl -vv 'http://target/getFlag'
4. 漏洞防御措施
4.1 安全的对象合并
- 使用现代JavaScript特性:
Object.assign({}, a, b)
-
使用安全的合并库(如lodash的
_.mergeWith) -
实现安全的合并函数:
function safeMerge(target, source) {
Object.keys(source).forEach(key => {
if (key === '__proto__' || key === 'constructor') {
return;
}
if (isObject(source[key]) && isObject(target[key])) {
safeMerge(target[key], source[key]);
} else {
target[key] = source[key];
}
});
return target;
}
4.2 其他防御策略
- 冻结原型:
Object.freeze(Object.prototype);
- 使用无原型对象:
const safeObj = Object.create(null);
- 输入验证:
- 检查JSON输入是否包含
__proto__或constructor属性 - 使用schema验证库
- 使用最新版本的库:
- 许多库已修复原型污染问题
5. 深入技术细节
5.1 原型链遍历过程
当访问对象属性时:
- 检查对象自身属性
- 如果不存在,通过
__proto__查找原型链 - 递归直到
Object.prototype或找到属性
5.2 污染影响范围
原型污染可以影响:
- 应用中的所有对象
- 第三方库创建的对象
- Node.js核心对象(如
global)
5.3 可能导致的安全问题
- 权限绕过(如示例中的admin检查)
- 拒绝服务(覆盖关键函数)
- 远程代码执行(污染后利用)
6. 实际开发建议
- 始终验证和净化用户输入的JSON
- 避免使用不安全的递归合并
- 考虑使用TypeScript增强类型安全
- 定期审计依赖库的安全性
- 实施严格的CSP策略