Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

前端安全-原型链污染 #21

Open
chiyan-lin opened this issue Oct 8, 2021 · 0 comments
Open

前端安全-原型链污染 #21

chiyan-lin opened this issue Oct 8, 2021 · 0 comments

Comments

@chiyan-lin
Copy link
Owner

常见的前端安全有 XSS、CSRF、恶意第三方库这几种,偶然发现了另外一种利用语言特性进行的攻击手段 -- 原型链污染。

既然是原型链污染,那就需要来说明下一个对象上面都有什么属性。

  1. [string or number]:本身自定义的属性,可以是函数,也可以是基础类型
  2. proto:实例上拥有的特殊属性,指向创建这个实例构造函数的原型
  3. constructor:指向创建实例的构造函数
  4. 通过 Object.prototype 继承而来的方法

对于一个我们直接声明或者是通过 new Object 而来的对象,proto 会指向 Object.prototype 。

根据 javascript 的属性读取规则,在原实例上查找不到的属性,会顺着原型链往下寻找,最终会找到 Object.prototype。

所以这里只要有办法对 proto 动手脚,那么就可以成功污染到所有从Object继承而来的对象。

实际操作

在实际应用中,哪些情况下可能存在原型链能被攻击者修改的情况呢?

哪些情况下我们可以设置__proto__的值呢?其实找找能够控制数组(对象)的“键名”的操作即可:

  • 对象merge
  • 对象clone(其实内核就是将待操作的对象merge到一个空对象中)

一件简单粗暴的 merge 方法

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操作进行原型污染

let o1 = {}
// 这里如果直接使用对象的形式,会不做效,因为 __proto__已经代表o2的原型了,只有使用 JSON.parse 才会被 for in 枚举到
let o2 = JSON.parse('{"a": 1, "__proto__": {"b": 2}}')
merge(o1, o2)
console.log(o1.a, o1.b)

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

所以在实践中要关注 merge方法的实现和使用,对用户传进来的参数要做好校验。比如在3.x版本 express 框架支持根据 Content-Type 来解析请求 Body,这里面就是用到了 JSON.parse 对参数进行对象转换,然后在进行对象合并,这里面就很容易进行原型链攻击。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant