Recent topics prototype met a lot of pollution, and did not learn to do a summary.
Source: Analysis and use Node.js prototype pollution attack and in-depth understanding of JavaScript Prototype pollution attack
Objects in JavaScript
The JavaScript object is the mapping between key and value, and is equivalent in python
dict
, for example, have the following code:var s = {name:"peri0d", age:"19"}; s.name; // print "peri0d" s.age; // print "19" typeof(s); // print "object" console.log(s); // print Object { name: "peri0d", age: "19" } console.log(s.prototype); // print undefined
If you perform
console.log(s)
will return the following information, you can see a lot of properties and methods are included inprototype
these extra information all in JavaScript from theObject
object, and the pythonObject
are all super class a reasonAll objects in JavaScript are through
Object
the object created. Its constructor for a given value creates an object wrapper. If the given value isnull
orundefined
will be created and returns an empty object, otherwise, it returns a value corresponding to the object type and give. When invoked in a non-constructor formObject
equivalentnew Object()
. In the following code example to illustratetest = Object.create(null); // print an empty object typeof(test); // print "object" console.log(test.prototype); // print undefined console.log(test); // print the whole object and all its attributes // print Object { } test2 = Object.create(String); typeof(test2); // print "object" console.log(test2..prototype); // print String { "" } console.log(test2);
JavaScript has no notion of absolute functions and classes, the constructor function itself as a class
When the function is created, it is added an
prototype
attribute, and common data types do not. This property is an object (prototype object) and has a defaultconstructor
attribute refers to a function, in this function, is a prototype object property.When an object is created, it is added a
__proto__
property to the newly created object, this attribute points to the constructor's prototype objectobject.__proto__
directionfunction.prototype
function person(fullName, age){ this.age = age; this.fullName = fullName; this.details = function(){ return this.fullName+" has age: "+this.age; } } console.log(person.prototype); /* {constructor: ƒ} constructor: ƒ person(fullName, age) __proto__: Object */ var person1 = new person("Alice", 22); var person1 = new person("Bob", 20); console.log(person1); /* person {age: 20, fullName: "Bob", details: ƒ} age: 20 details: ƒ () fullName: "Bob" __proto__: constructor: ƒ person(fullName, age) arguments: null caller: null length: 2 name: "person" prototype: {constructor: ƒ} __proto__: ƒ () [[FunctionLocation]]: VM672:1 [[Scopes]]: Scopes[1] __proto__: Object */ person1.details(); // print "Bob has age: 20"
prototype property and property __proto__
原型
prototype
是类的一个属性,而所有类实例化的对象,都将拥有这个属性中的所有内容,包括变量和方法function test(){ this.name = "peri0d"; } test.prototype.show = function show(){ console.log(this.name); } test1 = new test(); test1.show(); // print "peri0d"
一个类实例化出的属性可以通过
__proto__
来访问类的原型test1.__proto__ == test.prototype // print true
constructor 属性
它返回创建实例对象的
Object
构造函数的引用,即返回用于创建这个对象的函数。注意,此属性的值是对函数本身的引用,而不是一个包含函数名称的字符串举个例子
function Person() { this.color = "yellow"; this.showColor = function () { return this.color; } } console.log("Person.prototype.constructor: "+Person.prototype.constructor); /* Person.prototype.constructor: function Person() { this.color = "yellow"; this.showColor = function () { return this.color; } } */ var person1 = new Person(); console.log("person1.constructor: "+person1.constructor); /* person1.constructor: function Person() { this.color = "yellow"; this.showColor = function () { return this.color; } } */ // person1 实例通过 __proto__ 指向了 Person 的原型(Person.prototype),所以可以访问到 constructor Person.prototype = {}; // 修改 Person 的原型为 {},这里也可以看处 prototype 可以在运行时修改 var person2 = new Person(); console.log("person2.constructor: "+person2.constructor); /* person2.constructor: function Object() { [native code] } */ // person2 实例通过 __proto__ 指向了 Person 的原型(Person.prototype),即这个空对象,所以它访问到 constructor 为 Object
上面说到,原型对象有一个
constructor
,它指向函数本身而且constructor
的constructor
是一个全局函数constructor
console.log(person2.constructor.constructor); /* Global Function constructor print ƒ Function() { [native code] } */ person2.constructor.constructor("return 1"); /* ƒ anonymous( ) { return 1 } */ person2.constructor.constructor("return 1")(); // call this Global Function // print 1
总结一下
- 定义一个
function
时,会加上prototype
属性,表示它的原型对象,内含constructor
,返回这个function
的构造函数的引用,即函数本身。而这个constructor
的constructor
是一个全局函数constructor
- 定义一个实例化对象时,会加上
__proto__
属性,它等于function.prototype
,这个对象的constructor
就等于object.__proto__.constructor
,但是这两个constructor
代表的含义不同
- 定义一个
JavaScript 中的原型
函数的
prototype
可以在运行时修改function person(fullName, age) { this.age = age; this.fullName = fullName; } console.log(person.prototype); // {details: ƒ, constructor: ƒ} var person1 = new person("Anirudh", 25); person.prototype.details = function() { return this.fullName + " has age: " + this.age; } person.prototype.key="peri0d"; console.log(person.prototype); // print {details: ƒ, key: "peri0d", constructor: ƒ} console.log(person1.details()); // print "Anirudh has age: 25" console.log(person1.key); // print "peri0d"
在上面这段代码中我们通过添加一个新方法和属性的方式修改了函数的
prototype
,使用对象我们也可以达到同样的效果function person(fullName, age) { this.age = age; this.fullName = fullName; } var person1 = new person("Anirudh", 25); var person2 = new person("Anand", 45); person1.constructor.prototype.details = function() { return this.fullName + " has age: " + this.age; } // 上下两个相同 person1.__proto__.constructor.prototype.details = function() { return this.fullName + " has age: " + this.age; } console.log(person1.details()); // print "Anirudh has age: 25" console.log(person2.details()); // print "Anand has age: 45"
个人认为,可以将函数看为类的构造函数,原型看为类
JavaScript 原型链继承
所有类对象在实例化的时候将会拥有
prototype
中的属性和方法,这个特性被用来实现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}`) // print "Name: Melania Trump"
1.在对象 son 中寻找 last_name
2.如果找不到,则在 son.__proto__中寻找 last_name
3.如果仍然找不到,则继续在 son.__proto__.__proto__中寻找 last_name
4.依次寻找,直到找到 null 结束。比如,Object.prototype 的 __proto__ 就是 null这里为什么没有
Son.prototype = Father.prototype
,个人认为Father.prototype
指向的是一个类对象,并没有对属性进行实例化,所以Son
相当于没有继承Father
每个构造函数(
constructor
)都有一个原型对象(prototype
)object.__proto__
指向function.prototype
JavaScript 使用
prototype
链实现继承机制
原型链污染
举个例子
person = {name : "Jack"}; console.log(person.name); // print "Jack" person.__proto__.name = 'peri0d'; console.log(person.name); // print "Jack" person2 = {}; console.log(person2.name); // print "peri0d"
在
person
这个实例化对象中寻找name
属性,找到后输出。将
Object
类的name
属性赋值为peri0d
然后实例化person2
对象,在这个对象中没有找到name
属性,进而到__proto__
中去寻找,找到后输出。这实际上是修改了Object
类在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象
可存在于对象合并和对象克隆中,找可以控制对象的属性的操作
例子
以
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] } } }
考虑如下代码,可以看到,
merge
并没有修改__proto__
下Object
的内容,而是将b
作为其属性添加,这样再生成新对象的时候还是原来的Object
o1 = {}; o2 = {a: 1, "__proto__": {b: 2}}; merge(o1, o2); console.log(o1); // print {a: 1, b: 2} o3 = {}; console.log(o3.b); // print undefined for(let key in o2){ console.log(key); } // print a b
将代码修改一下,这时
Object
已被污染o1 = {}; o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}'); merge(o1, o2); console.log(o1.a, o1.b); // print 1 2 o3 = {}; console.log(o3.b); // print 2
JSON 解析的情况下,__proto__ 会被认为是一个真正的“键名”,而不代表“原型”,所以在遍历 o2 的时候会存在这个键。