nodejs(原型链污染)浅入

狠狠污染链,狠狠的继承类

原型与原型链

想要原型链污染,先要了解什么是原型与原型链

Javascript中一切皆是对象,其中对象之间是存在共同和差异的,比如对象的最终原型是Object是原型null,函数对象有prototype属性,但是实例对象没有。

原型的定义

JavaScript原型是一种对象继承的机制,它允许对象共享属性和方法

每个对象(除了null)都有一个原型对象,它可以通过__proto__属性或Object.getPrototypeOf()方法来访问。

每个函数对象(除了箭头函数)都有一个prototype属性,它指向该函数作为构造函数时创建的实例对象的原型。

原型就是一个对象,它可以给其他对象提供共用的属性和方法。比如,你有一个人类的原型,它有姓名、年龄、说话等属性和方法,那么你可以用这个原型来创建很多人的对象,他们都会继承这些属性和方法。

原型链的定义

js 是由对象组成的,对象与对象之间存在着继承关系

每个对象都有一个指向它的原型的内部链接,而这个原型对象又有他自己的原型,直到 null 为止

整体看来就是多个对象层层继承,实例对象的原型链接形成了一条链,也就是 js 的原型链

原型之间也可以相互继承,比如你有一个学生类的原型,它继承了人类的原型,但是它还有自己的属性和方法,比如学号、学习等。那么你可以用这个学生类的原型来创建很多学生的对象,他们既会继承人类的属性和方法,也会继承学生类的属性和方法。这样就形成了一个原型链,它决定了对象之间的关系。

原型对象的定义

原型对象是JavaScript中的一种特殊的对象,它可以被附加到其他对象或函数上,从而实现对象之间的继承和共享。原型对象本身也是一个对象,它有自己的原型,形成一个原型链。原型链在遇到null为原型的对象时结束。您可以通过设置或修改一个对象或函数的prototype属性来改变它的原型。

1
2
3
4
5
6
>// 创建一个空对象
var obj = {};
>// 设置obj的原型为另一个对象
obj.__proto__ = {name: "Alice", age: 20};
// 访问obj的name属性,实际上是访问它的原型的name属性
console.log(obj.name); // Alice

可以直接在控制台中进行

image-20230826164140299

在JavaScript中,声明一个函数A的同时,浏览器在内存中创建一个对象B,然后A函数默认有一个属性prototype指向了这个对象B,这个B就是函数A的原型对象也叫函数A的原型。这个对象B默认会有个属性constructor指向了这个函数A。

如下图中,函数Fooprototype指向{constuctor:f},而{constuctor:f}的属性constuctor指向函数Foo

image-20230827094453409

实例对象

我们可以通过构造函数A构建一个实例对象A,A默认会有一个属性__proto__指向了构造函数A的原型B。

原型对象和实例对象的关系

如下图中,fooFoo函数的实例化对象,foo__proto__刚好等于Foo函数的原型,返回为true

image-20230827095320595

原型链机制

概念可以看一下

每个构造函数都有一个原型,原型都包含一个指向构造函数的指针(constructor),

而实例都包含一个指向原型的内部指针(__proto__)。

假如我们将一个构造函数的 prototype 属性设置为另一个类型的实例,那么该构造函数的原型对象将会指向另一个类型的原型。于是,这个新的原型对象也将包含一个指向另一个构造函数的 prototype 对象的内部指针(__proto__)。如此层层递进,就形成了一个由实例和原型构成的原型链。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
function Animal(name) {
this.name = name;
}

Animal.prototype.sayHello = function() {
console.log('Hello, my name is ' + this.name);
};

function Dog(name) {
Animal.call(this,name);
}

// 将 Dog 的原型设置为 Animal 的实例
//Object.create 创建一个新的对象,这个对象的原型指向 Animal.prototype,从而实现继承。
//Dog.prototype = Animal.prototype;这样会出现什么问题?两者会进行共享原型对象(Dog.prototype),这样修改Dog.prototype也会修改Animal.prototype中的内容
Dog.prototype = Object.create(Animal.prototype);
Dog.prototype.constructor = Dog;

Dog.prototype.bark = function() {
console.log('Woof! Woof!');
};

let dog = new Dog('aa');

dog.sayHello(); // Hello, my name is aa
dog.bark(); // Woof! Woof!

console.log(dog.__proto__ === Dog.prototype); // true
console.log(Dog.prototype.__proto__ === Animal.prototype); // true
console.log(Animal.prototype.__proto__ === Object.prototype); // true

概念太绕了,解析一下图片

Person作为构造函数,每一个构造函数都有一个原型对象Person.prototype,而原型对象都包含一个指向构造函数的指针[constructor]

而其中person作为Person构造函数的实例[let person = new Person();],也包含一个指向原型对象的内部指针[__proto__]

于是最后这个原型链就是

person -> Person.protype -> Object.prototype -> null

可以看到原型链的结尾就是null

过滤绕过

__proto__

constructor.prototype