原型链污染[JavaScript]

一、原型链污染

此类型一般存在以nodejs编写的后端程序当中,其中Express是一个流行的Node.js Web应用程序框架

1.JavaScript

1.1 原理

引入

解释:直接先读代码来理解原型链污染

// 
let jack = {'b':2}
console.log(typeof jack) // 它的类型是obejct
console.log(jack.b)// 2
jack.__proto__.b='3'
bug = {}
console.log(bug.b) // 3 这里我们可以看到,明明bug并没有被赋值,为什么是3呢
// 如果看到这里不清楚为什么bug.b为3 请看下面关于继承的知识

继承

function Son() {
    this.first_name = 'Melania';
}

let son = new Son();
console.log(son.last_name)

解释:通过上面的例子,我们来粗略的解释一下继承,当我们newSon()之后,我们调用了son.last_name,此时在对象son中寻找last_name,无法找出,则在son.__proto__中寻找last_name(下面第一张图片打印了son的信息),如果还是没有找到就从son.__proto__.__proto__中寻找,依次类推,直到__proto__的值为undefined

原型链污染[JavaScript]_第1张图片

解决

解释:有了上面知识的补充我们再来看下面的代码,jack.b值为2,当我们修改了jack.__proto__.b='3'时,相当于我们修改了object隐式的继承关系,也就是说bug此时在其当中并没有找到b,但是在其继承关系里面找到了(如下图),我们可以看到bug.__proto__里面存在b也就是bug.__proto__.b='3'所以我们就得到了bug.b=3

let jack = {'b':2}
console.log(jack.b)// 2
jack.__proto__.b='3'
let bug = {}
console.log(bug.b) // 3 

原型链污染[JavaScript]_第2张图片

疑问:jack.__proto__.b='3'修改了其的值,再新建一个object对象为什么会受其影响
解答:使用 jack.__proto__.b = '3' 这样的代码时,实际上是在修改 jack 对象原型(也就是 Object.prototype)上的属性 b。这是因为jack.__proto__实际上指向的是 jack 对象的原型对象,对于由字面量方式创建的对象,它们的原型就是 Object.prototype

1.2 案例

// post请求的路径
app.post('/register', (req, res) => {
 
    let user = JSON.parse(req.body)  // 把我们输入的账号密码,从json字符串转成对象
 
    // 判断我们有没有输入账号和密码
    if (!user.username || !user.password) {  
        return res.json({ msg: 'empty username or password', err: true })
    }
    // 判断账号是否存在总对象的username里,如果相同的username就是重复用户名了
    if (users.filter(u => u.username == user.username).length) {  
        return res.json({ msg: 'username already exists', err: true })
    }
    // isAdmin是否true 与 邀请码是不是等于这个常量,所以sql注入没用,邀请码是个常量
    if (user.isAdmin && user.inviteCode != INVITE_CODE) {
        user.isAdmin = false
        return res.json({ msg: 'invalid invite code', err: true })
    }
 
    // 使用系统函数复制对象,打包成一个新的对象
    let newUser = Object.assign({}, baseUser, user)
    users.push(newUser)  // 存到总对象里
    res.json({ msg: 'user created successfully', err: false })  // 设置返回信息
})

相关知识:

  • 代码
      // Object.assign基础用法
      const target = { a: 1, b: 2 };
      const source = { b: 4, c: 5 };
      const result = Object.assign(target, source);
      console.log(result); // { a: 1, b: 4, c: 5 }
      // Object.assign存在的原型链漏洞
      const source = '{ "__proto__":{"b": 4} }'
      const dsource = JSON.parse(source)
      console.log(dsource)
      const result = Object.assign({}, dsource);
      console.log(result)
      console.log(result.b)
      
  • 结果
    • dsource:
      在这里插入图片描述
    • result:
      在这里插入图片描述

题解:由let newUser = Object.assign({}, baseUser, user)我们知道,其将里面三项内容进行了拼接,其中Object.assign存在我们可以输入的user如果我们将其里面的属性恶意构造将造成了原型链污染,在此题中传入的json只要包含{ "__proto__":{"isAdmin": true}我们就会有了Admin权限(因为我们输入的字段并不存在isAdmin所以其会在obejct隐式继承里面找到被我们污染的属性{"isAdmin": true})

你可能感兴趣的:(#,Web安全,javascript,开发语言,ecmascript)