一、原型链污染
此类型一般存在以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)
解释:通过上面的例子,我们来粗略的解释一下继承,当我们new
了Son()
之后,我们调用了son.last_name
,此时在对象son
中寻找last_name
,无法找出,则在son.__proto__
中寻找last_name
(下面第一张图片打印了son的信息),如果还是没有找到就从son.__proto__.__proto__
中寻找,依次类推,直到__proto__
的值为undefined
解决
解释:有了上面知识的补充我们再来看下面的代码,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
疑问:
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 = falsereturn 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:
- dsource:
题解:由let newUser = Object.assign({}, baseUser, user)
我们知道,其将里面三项内容进行了拼接,其中Object.assign
存在我们可以输入的user
如果我们将其里面的属性恶意构造将造成了原型链污染,在此题中传入的json
只要包含{ "__proto__":{"isAdmin": true}
我们就会有了Admin权限(因为我们输入的字段并不存在isAdmin
所以其会在obejct隐式继承里面找到被我们污染的属性{"isAdmin": true}
)