JS原型链污染

JS 原型链污染

参考连接:

一文搞懂JS原型与原型链(超详细,建议收藏) - 掘金 (juejin.cn)

js原型链污染(超详细)_javascript原型链污染_l_abour的博客-CSDN博客

前置知识

  1. JS的复杂类型都是对象类型(Object),作为不是完全的面对对象编程的语言,需要使用构造函数进行继承的机制

    例子:

    1
    2
    3
    4
    5
    6
    7
    8
    // 构造函数
    function Person(name, age) {
    this.name = name;
    this.age = age;
    }

    // 生成实例
    const p = new Person('zhangsan', 18);
  2. 对于无法共享公共属性的问题,使用原型对象,来存储这个构造函数的公共属性以及方法。(主角登场)

  3. 构造函数的prototype以及每个通过构造函数创建出来的实例对象,其本身有个属性__proto__,这个属性会指向该实例对象构造函数原型对象

就是说如图所示

image-20230825191928584

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
function Preson(name, age) {
this.name = name;
this.age = age;
}
// 所有实例共享的公共方法
Preson.prototype.say = function (word) {
console.log(`${this.name}说:${word}`);
}

const p1 = new Preson('张三', 18); // 创建一个Person实例对象
const p2 = new Preson('李四', 20); // 新创建一个Proson实例对象
p1.say('hello world'); // 调用公共方法
p1.hasOwnProperty('say') // false 说明不是定义在其本身上的
p1.__proto__.do = function () {
console.log('往原型对象中添加方法');
}
p2.do(); // 打印出了-往原型对象中添加方法

利用

基于上述原理,存在的漏洞点是不安全的对象递归合并,只有不安全的递归合并函数才会导致原型链污染,非递归的算法是不会导致原型链污染的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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, o1.b); //1,2

o3 = {};
console.log(o3.b); //2

注意:let o2 = {a: 1, “__proto__”: {b: 2}}中的proto已经作为的是o2的原型,也就是说这里o2[__proto__]={b:2}(一个对象)并没有被解析成为键名,所以需要JSON.parse将其转成json格式

第一步o1[‘a’]=o2[‘a’]

第二步,会将__proto__当作键值来看,然后进入if语句中,然后o1['__proto__']['b']=o2['__proto__']['b'],然后相当于o1.__proto__.b=2成功污染