JavaScript原型链污染探讨
字数 1086 2025-08-20 18:18:04
JavaScript原型链污染详解
一、基本概念
1.1 __proto__与prototype的区别
-
__proto__:- 每个对象都有的隐藏属性
- 指向创建该对象的构造函数的原型对象(
prototype) - 是对象用于继承属性和方法的机制
- 所有对象都可以通过
__proto__访问其原型,实现原型链查找
-
prototype:- 是函数(特别是构造函数)特有的属性
- 用于定义由该构造函数创建的实例共享的属性和方法
- 通常用来定义构造函数的实例方法
- 新创建的对象会继承其构造函数
prototype上的属性和方法
const obj = {};
console.log(obj.__proto__ === Object.prototype); // true
1.2 原型链机制
JavaScript中的继承是通过原型链实现的:
- 每个对象都有一个指向其原型的内部链接(
__proto__) - 原型本身也是一个对象,通常还有自己的原型
- 形成一条原型链
- 访问对象属性时,JavaScript会沿着原型链逐级查找
二、原型链示例
function Animal(name) {
this.name = name;
}
function Something() {
this.speak = function() {
console.log(this.name + ' makes a beautiful sound.');
};
};
const dog = new Animal('Dog');
dog.speak(); // 输出: 'Dog makes a beautiful sound.'
原型链关系:
dog -> Animal.prototype -> Something.prototype -> Object.prototype -> null
三、原型链污染原理
通过修改对象的原型(__proto__),可以影响所有与该对象来自同一个类、父类直到Object类的对象。
3.1 污染示例
function Animal(name) {
this.name = name;
}
function Something() {
this.speak = function() {
console.log(this.name + ' makes a beautiful sound.');
};
};
const dog = new Animal('Dog');
dog.__proto__.speak = function() {
console.log('修改成功');
};
dog.speak(); // 输出: '修改成功'
3.2 典型污染场景:merge操作
function merge(target, source) {
for (let key in source) {
if (key in source && key in target) {
merge(target[key], source[key])
} else {
target[key] = source[key]
}
}
}
当攻击者可以控制source对象时,可以通过设置__proto__属性来污染原型链:
let obj = {};
merge(obj, {
__proto__: {
polluted: true
}
});
// 现在所有对象的原型都被污染了
console.log({}.polluted); // true
四、防御措施
-
避免使用
__proto__:- 使用
Object.getPrototypeOf()和Object.setPrototypeOf()替代
- 使用
-
安全的对象合并:
- 检查属性名是否为
__proto__或constructor等敏感属性 - 使用
Object.hasOwnProperty()确保只处理对象自身的属性
- 检查属性名是否为
-
冻结原型:
- 使用
Object.freeze(Object.prototype)防止原型被修改
- 使用
-
使用Map代替Object:
- Map不会受到原型链污染的影响
-
输入验证:
- 严格验证用户输入,特别是用于构建对象的JSON数据
五、实际应用场景
-
Node.js应用:
- 污染全局对象原型可能导致所有模块受到影响
-
前端框架:
- 污染可能导致不可预测的行为或XSS漏洞
-
API处理:
- 不安全的JSON解析可能导致原型链污染
六、检测方法
-
检查对象原型:
console.log(Object.prototype.polluted); // 检查是否被污染 -
使用安全模式:
- 启用严格模式(
'use strict')可以帮助发现一些问题
- 启用严格模式(
-
代码审计:
- 检查所有对象合并、复制操作的安全性
理解原型链污染对于JavaScript开发者至关重要,特别是在构建需要处理不可信输入的应用程序时。通过遵循安全实践和防御措施,可以有效防止这类安全问题。