JavaScript原型链污染攻击
JavaScript原型链污染初探,题目待补充···
# 原型对象和原型链
JavaScript虽然是面向对象的语言,但是不同于其他语言的是其本身并没有class关键字来进行类的构造。下面是使用构造函数创建的一个对象。
//Cat就是一个构造函数
function Cat() {
this.name = 'F4de'
}
var cat = new Cat()
console.log(cat.name)
2
3
4
5
6
7
# (1)prototype
function Cat() {
this.name = 'F4de'
}
Cat.prototype.color = 'black'
var cat = new Cat()
console.log(cat.name)
console.log(cat.color)
2
3
4
5
6
7
在JavaScript中每一个函数都有一个prototype属性,该属性指向了一个对象,该对象就是调用该构造函数而创建的实例的原型。
什么是原型呢?简单来说,每一个JavaScript对象(null除外)创建的时候就会自动关联另外一个对象,这个对象就是所说的原型,每一个对象都会从对应的原型中继承属性。

# (2)__proto__
上文中提到,构造函数的prototype属性指向对应实例的原型,也就是说,prototype是用来关联构造函数和实例原型之间的桥梁。
那么该如何关联实例和实例原型呢?每一个JavaScript对象(null除外)都有一个属性,叫做__proto__,该属性会指向实例的原型。
也就是说,构造方法的prototype和实例的__proto__是等价的,我们可以通过下面的代码证明:
function Cat() {
this.name = 'F4de'
}
var cat = new Cat()
console.log(Cat.prototype == cat.__proto__)
2
3
4
5
用关系图来表示会更加显而易见:

# (3)constructor
prototype和__proto__分别是构造函数和实例来单向指向原型的属性,那么原型是否有方法来指向构造函数和实例呢?
原型有指向构造方法属性constructor,而没有指向实例的属性,因为一个构造函数可以生成多个实例化对象。
function Cat() {
this.name = 'F4de'
}
var cat = new Cat()
console.log(Cat.prototype.constructor == Cat)
console.log(cat.__proto__.constructor == Cat)
2
3
4
5
6
用关系图来表示:

# (4)实例和原型
当读取实例的属性时,如果找不到实例的属性,就会向上查找与实例相关联的原型的属性,如果还查不到,就再向上查找原型的原型的属性,一直到最顶层null为止。
function Cat() {
this.name = 'F4de'
}
var cat = new Cat()
cat.__proto__.color = 'black'
console.log(cat.color)
2
3
4
5
6
因为cat没有color属性,所以会向上查找原型对象是否有该属性,我们已经向原型对象中添加了color属性,所以cat.color可以正常输出。
# (5)原型的原型
原型本身就是一个对象,我们可以用最原始的方式来创建:
var obj = new Object()
obj.name = 'F4de'
console.log(obj.name)
//输出结果F4de
2
3
4
原型对象就是通过Object构造函数来生成的,结合之前所描述的那样,一个对象的__proto__指向了该对象的原型,也就是说,原型对象的__proto__属性指向了原型的原型,关系图如下:

而Object.prototype的原型是null,也就走到了原型对象继承的最顶层。
var obj = new Object()
obj.name = 'F4de'
console.log(Object.prototype.__proto__ == null)
2
3
4
关系图表示:

[cat --> Cat.prototype --> Object.prototype --> null] 这样的链状结构就是原型链。
# 原型链污染攻击
下面开始进入正题,开始学习JavaScript中的原型链污染攻击。
# (1)初探
先看以下代码:
function Cat() {
}
var cat1 = new Cat()
cat1.__proto__.color = 'red'
console.log(cat1.color)
console.log(cat1.__proto__)
console.log(cat1)
var cat2 = new Cat()
console.log(cat2.color)
2
3
4
5
6
7
8
9
10
11
12
运行结果:
在Cat类中本没有color这个属性,但是我们通过修改cat1.__proto__.color让cat1对象的原型多出了一个color属性,从而影响到了后面与该原型相关联的对象,让其也具有了该属性。
如果可以控制对象原型,从而影响所有同类对象的行为,就被成为原型链污染。
# (2)常用实现方法
**Tips:**在JavaScript中访问一个对象的属性可以用a.b.c或者a["b"]["c"]来访问。由于对象是无序的,当使用第二种方式访问对象时,只能使用指明下标的方式去访问。因此我们可以通过a["__proto__"]
的方式去访问其原型对象。
最常见的一种方法,就是通过对象键名来赋值:
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]
}
}
}
2
3
4
5
6
7
8
9
两个数组在合并的过程中,存在target[key] = source[key]这样通过数组键名来赋值的情况,如果可以控制key = __proto__,那么就可能存在原型链污染的情况。
(未完······)
在学了在学了💀