JS原型链污染学习笔记

网安小白在学习原型链污染时一头雾水,参考了大佬们的文章,在这里记录一下学习笔记,方便日后复习,若有错误还请指正

 JS中的对象(Object)是一种复合数据类型,它是一种无序的键值对集合。对象用于存储和传递多个值,每个值都有一个键(key)与之关联。

几种常见的对象的创建方式

第一种: 对象字面量方式
var obj1 = {
  name: "Jack",
  age: 26,
}
 
 
第二种: Object构造函数模式
var obj2 = new Object()
obj2.name = "Jack"
obj2.age = 26 
 
 
第三种: 构造函数模式
function Test(name, age){
    this.name = name
    this.age = age
    this.say = function(){
        console.log('我能说话')
    }
}
var obj3 = new Test('Jack', 26)
var obj4 = new Test('Rose', 25)
 
 


 

1.__proto__

__proto__ 是每个对象在被创建时都有的一个属性,它指向该对象的原型(即构造函数的 prototype 属性 XX.prototype)通过 __proto__,JavaScript 实现了原型链,当访问一个对象的属性时,如果该对象没有这个属性,就会沿着原型链向上查找,直到找到该属性或到达原型链的顶端(null

let obj = {};
console.log(obj.__proto__ === Object.prototype); // true

2.prototype

prototype 是每一个对象(包括函数)所拥有的属性,它指向另一个对象,这个对象被称为原型对象,即xx.prototype,用来实例共享的属性和方法。每个函数在创建时都会自动获得一个 prototype 属性。

function Person(name) {
    this.name = name;
}

Person.prototype.sayHello = function() {
    console.log(`Hello, my name is ${this.name}`);
};

let alice = new Person('Alice');
alice.sayHello(); // 输出:Hello, my name is Alice

上面的例子中,Person.prototype原型对象包含了sayHello方法,所有由 Person 构造函数创建的实例都可以访问这个方法。

原型对象有一个constructor属性指向构造函数本身

Test.prototype.constructor === Test
// true

原型对象是一个普通的对象,它包含属性和方法。如上面的sayHello方法

原型对象的属性和方法会被继承到所有通过原型链与它相连的对象。

把方法写在原型对象上的好处是,所有实例在被创建出时便可使用次方法,而不用写到构造函数中占用内存

3.constructor

constructor(构造), 是每个对象都有的一个属性,它指向创建该对象的构造函数。通过 constructor 属性,我们可以知道某个对象是由哪个构造函数创建的。

console.log(alice.constructor === Person); //true

 原型链污染

定义:如果攻击者控制并修改了一个对象的原型,那将可以影响所有和这个对象来自同一个类、父类的对象,这种攻击方式就是原型链污染

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操作是最常见的可以控制键名的操作,也最能被原型链攻击。在合并过程中,复制操作的key如果为__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];
        }
    }
}
let o1 = {};
let o2 = {a:1,"__proto__":{b:2}};
merge(o1,o2);
console.log(o1.a,o1.b);//1 2

console.log(o1.__proto__ == o2.__proto__)//false

console.log(o1)//{ a: 1, b: 2 }
console.log(o2)//{ a: 1 }

o3 = {};
console.log(o1.__proto__ == o3.__proto__)//true

console.log(o2.__proto__)//{ b: 2 }
console.log(o3.b);//undefined

分析:当对o1,o2进行merge操作:merge(o1,o2)时,console.log(o1.a,o1.b)均有值1,2,这是否说明o1.__proto__已经被污染了呢?当我们创建o3对象时,console.log(o3.b)为undefined,说明并未发生原型链污染。o1.b之所以等于2,是因为在merge过程中,键__proto__虽然存在于o2中,但merge函数并没有对这个键进行特殊处理,它直接将source[key](即o2["__proto__"])的值{b:2}赋值给了o1,没有为o1添加一个新的__proto__键,所以最终得到了一个b属性。

运行如下代码

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];
        }
    }
}
let o1 = {};
let o2 = JSON.parse('{"a":1,"__proto__":{"b":2}}');
merge(o1,o2);
console.log(o1.a,o2.b);// 1 2
 
o3 = {};
console.log(o3.b);// 2

分析:在进行JSON解析的情况,merge函数进行合并的过程中,o2的__proto__将作为"键名"而不作为特殊属性被merge函数把o2的 __proto__ 属性被合并到 o1 中。产生原型链污染,

但是此时o1__proto__不等于o2__proto__,因为它们只是内容相同,而属于不同的对象实例。o1__proto__ 等于o3__proto__

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值