JS prototype pollution

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 in prototypethese extra information all in JavaScript from the Objectobject, and the python Objectare all super class a reason

  • All objects in JavaScript are through Objectthe object created. Its constructor for a given value creates an object wrapper. If the given value is nullor undefinedwill 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 form Objectequivalent new Object(). In the following code example to illustrate

    test = 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

    1. When the function is created, it is added an prototypeattribute, and common data types do not. This property is an object (prototype object) and has a default constructorattribute refers to a function, in this function, is a prototype object property.

    2. When an object is created, it is added a __proto__property to the newly created object, this attribute points to the constructor's prototype object

    3. object.__proto__ direction function.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 ,它指向函数本身而且 constructorconstructor 是一个全局函数 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
  • 总结一下

    1. 定义一个 function 时,会加上 prototype 属性,表示它的原型对象,内含 constructor ,返回这个 function 的构造函数的引用,即函数本身。而这个 constructorconstructor 是一个全局函数 constructor
    2. 定义一个实例化对象时,会加上 __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 的时候会存在这个键。

Guess you like

Origin www.cnblogs.com/peri0d/p/11519533.html
Recommended