1.什么是原型链污染
原型链污染(Prototype Pollution)是一种web应用程序中常见的安全漏洞,主要影响使用JavaScript的应用程序。它是一种攻击技术,通过修改JavaScript对象的原型链,来实现对应用程序的非法操作和控制。
JavaScript中的对象是通过原型链进行继承的。每个对象都有一个指向其原型的链接,它允许对象从其原型中继承属性和方法。当JavaScript代码在处理输入时,如果不适当地允许用户控制原型链上的属性,攻击者就可以操纵原型链,并在目标对象上添加、修改或删除属性。
攻击者利用原型链污染可以实现多种攻击,包括但不限于:
- 修改对象的原型,从而覆盖或扩展原有属性和方法,导致应用程序的意外行为或漏洞。
- 污染全局对象的原型,导致全局范围内的异常行为,可能导致系统崩溃或敏感信息泄露。
- 在原型链上添加恶意方法或属性,用于劫持或篡改应用程序的逻辑。
- 绕过应用程序的安全控制,获取未授权的访问权限。
原型链污染漏洞通常是由于开发者未正确验证和过滤用户提供的输入数据所导致的。为了防止原型链污染,开发者应该始终对用户输入进行严格的验证和过滤,并避免直接使用用户提供的数据来操作原型链上的属性。另外,更新JavaScript运行时环境和库到最新版本也可以帮助防范已知的原型链污染漏洞。
在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。
2.原型链三属性
1)prototype
prototype
是函数对象特有的属性。在 JavaScript 中,每个函数都有一个
prototype
属性,它是一个指向对象的引用。这个对象被称为该函数的原型对象,它包含了可以由该函数的所有实例共享的属性和方法。当通过构造函数创建对象实例时,实例的
__proto__
属性会指向构造函数的
prototype
对象。
2)constructor
constructor
属性是原型对象的一个属性,它指向构造函数本身。当你创建一个函数并给它设置
prototype
属性后,
prototype
对象会自动获得一个名为
constructor
的属性,该属性指向该函数本身。这样,通过实例对象的
constructor
属性,你可以访问到创建该实例的构造函数。
3)proto
__proto__
是每个对象都有的属性,它指向对象的原型。在 JavaScript 中,对象通过
__proto__
属性链接到它们的原型对象,从而形成了原型链。当你访问一个对象的属性时,如果该对象本身没有该属性,JavaScript 就会沿着原型链往上查找,直到找到该属性或者到达原型链的顶端(通常是
Object.prototype
)。
4)原型链三属性之间关系
// 创建一个构造函数
function Person(name) {
this.name = name;
}
// 给构造函数的 prototype 添加一个方法
Person.prototype.sayHello = function() {
console.log('Hello, I am ' + this.name);
};
// 创建一个实例
const john = new Person('John');
// 使用 __proto__ 访问原型对象
console.log(john.__proto__ === Person.prototype); // true
// 使用 constructor 访问构造函数
console.log(john.constructor === Person); // true
// 使用 Object.getPrototypeOf() 访问原型对象
console.log(Object.getPrototypeOf(john) === Person.prototype); // true
// 使用原型链调用方法
john.sayHello(); // 输出: "Hello, I am John"
3.JavaScript原型链继承
function Father() {
this.first_name = 'Donald'
this.last_name = 'Trump'
}
function Son() {
this.first_name = 'Melania'
}
Son.prototype = new Father()
let son = new Son()
console.log(`Name: ${son.first_name} ${son.last_name}`)
1)分析
Son类继承了Father类的
last_name
属性,最后输出的是
Name: Melania Trump
2)总结
- 在对象son中寻找last_name
- 如果找不到,则在
son.__proto__
中寻找last_name- 如果仍然找不到,则继续在
son.__proto__.__proto__
中寻找last_name- 依次寻找,直到找到
null
结束。比如,Object.prototype
的__proto__
就是null
3)运行结果
4.原型链污染简单实验
1)实验一
let foo = {bar :1}
console.info(foo.bar)
//foo是一个object foo.__proto__ === object.prototype
foo.__proto__.bar = 2
console.info(foo.bar)
let zoo = {}
console.info(zoo.bar)
污染过程
因为前面我们修改了foo的原型
foo.__proto__.bar = 2
,而foo是一个Object类的实例,所以实际上是修改了Object这个类,给这个类增加了一个属性bar,值为2。
后来,我们又用Object类创建了一个zoo对象
let zoo = {}
,zoo对象自然也有一个bar属性
2)实验2
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]
}
}
}
var x = {
age:11
}
var y = {
age: 12,
num: 100
}
merge(x,y)
console.info(x)
console.info(y)
运行结果:
为什么会被污染?
merge
函数的目的是将
source
对象中的属性合并到
target
对象中。如果
source
对象和
target
对象具有相同的键(属性名),那么
merge
函数将递归地将嵌套对象的属性合并。否则,如果
source
对象具有
target
对象中不存在的键,
merge
函数将直接将该键值对添加到
target
对象中,接下来,执行
merge(x, y)
语句将会将
y
对象的属性合并到
x
对象中
版权归原作者 沐芊屿 所有, 如有侵权,请联系我们删除。