介绍
面试题经常会提到一个问题,就是前端安全问题哪些,我们基本上都回答上来以下几个:
- XSS脚本攻击,利用网站漏洞,注入非法脚本
- CSRF跨站请求伪造,攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求
- 运营商劫持,一般是经过某些运营商DNS网关后,在外层套入iframe,然后实现页面劫持
但是在前端安全中,还有一个问题就是 :JS原型链污染
, 那么是原型链污染怎么回事,请看下面详细介绍。
这里插入一条新闻,新出一个很邪恶的js库叫Evil.js,名字还取得很lodash,其中主要就是利用原型prototype进行重写项目中常用的方法,如:
- Array.map 有5%概率会丢失最后一个元素,
- 当数组长度可以被7整除时,Array.includes 永远返回false。
所以学会如何防御项目内被攻击,了解更多JS原型安全知识很重要,起码周末不用调试半天问题,才发现项目中代码被恶意写入。
在看这篇文章,请确保自己对JS原型和原型链有一定了解,如果忘记或缺乏了解的话,可以看我之前的文案从null、undefined、NaN的区别了解JS的原型链再次复习和学习一下。
原型链污染
是什么
下面通过一段代码,我们能够快速了解原型链是如何污染的?
1 | const foo = { |
从上述代码可以概括一下,原型链攻击是:
在一个应用中,如果攻击者控制并修改了一个对象的原型,那么将可以影响所有和这个对象来自同一个类、父祖类的对象。这种攻击方式就是原型链污染。
攻击场景
其实我们主要看哪些场景会允许代码设置__proto__
?主要有以下两种:
- 对象merge
- 对象clone
- Node.js的construtor
merge demo案例
一般实现对象的merge代码实现如下:
1 | function merge(target, source) { |
这是为什么呢?因为声明变量的时候,”_proto“并不是一个key,即使修改也不会改变Object的原型。
那么如何做到”_proto“成为一个key,可以将demo代码调整一下,如:
1 | let o1 = {} |
merge操作是最常见可能控制键名的操作,也最能被原型链攻击,很多常见的库都存在这个问题。如: lodash.merge
。
实际应用问题
Code-Breaking 2018 Thejs 分析
1 | // ... |
题目是利用lodash.template
+ lodash.merge
两个方法实现部分功能:
- 用户提交的信息,用merge方法合并到session里,session里最终保存你提交的所有信息
- 然后利用merge方法注入原型污染
- 最后利用template中的方法,完成整个网站污染
整个案例其实比较重要的一点就是,提交参数的时候序列化提交参数是否有做防御,如何没有的话那么很容易就中招了。
如何防御
分不同情况做防御:
引入npm包导致的
- 项目代码扫描,主要是针对构建好的代码进行扫描,是否
Evil.js
修改内置原型的情况进行告警提示 - 禁止修改原型,如:使用
Object.freeze
针对node.js
- 针对node服务端接口参数进行序列化的时候,禁止非法
key
值传入 - 禁止修改原型,如:使用
Object.freeze
- 采用
Object.create(null)
创建对象,避免直接使用{}
- 使用
map
数据类型创建 - 不对参数做
JSON.parse
转换,采用封装后的反序列化方法 - 尽量采用安全的
merge
或clone
库方法
参考资料
- 本文作者: Qborfy
- 本文链接: https://www.qborfy.com/today/20230209.html
- 版权声明: 本博客所有文章除特别声明外,均采用 MIT 许可协议。转载请注明出处!