y18n引发安全漏洞,警惕javascript原型链污染

简介: 前端的安全问题不容忽视,及时的升级版本很重要,即便是非常微不足道的小组件都可能应发安全问题,来看一看y18n,它仅仅是一个用于处理i18n的npm包而已,却也能爆出高危漏洞。

2021-04-01收到了github的邮件,还以为是关于愚人节的(误),提醒我有一条Dependabot alerts,包依赖存在安全问题,当然这不是第一次了,正常情况我批准merge掉后就不会关心了,不过我发现这个包是y18n,这让我感到奇怪,这个包的功能非常简单,其实就是i18n,做语言国际化用的,逻辑应该非常简单,怎么会出安全问题呢?

我找到了这个安全问题的具体信息,他的CVE ID为CVE-2020-7774,并且上报了SNYK,代码为SNYK-JS-Y18N-1021887,SNYK对此安全问题给出了7.3的高分,并将安全类别划分为CWE-400(资源消耗不受限制),如此严重的问题是怎么回事呢?

找到具体的修复PR,在https://github.com/yargs/y18n/pull/108,commit只有一个,为https://github.com/yargs/y18n/pull/108/commits/e90e8ce71b2fd5aa27c5109884ea47525fde961f,打开这个commit,发现其实主要只改了一行代码,加了两个检测原型的单元测试,这一行代码修改如下

-    this.cache = {
   }
+    this.cache = Object.create(null)

可能有人不明白这两行代码有什么区别,使用{}创建的对象自带原型属性,而Object.create(null)是干净的,在console中就可以试出来,输入{},点开返回的内容,发现其中只有一个__proto__属性,点开这个属性可以发现其中还有constructorhasOwnProperty等等,而输入Object.create(null),返回的是一个没有任何属性的空的{}

为什么带__proto__的对象有安全隐患呢,原因很简单,__proto__是所有对象共享的,而不是一个对象独占,举一个很简单的例子,在console运行如下code

let foo = {
   }
let bar = {
   }
foo.__proto__.isBad = true
bar.isBad
//true

我们发现bar.isBad得到了true,其实此时任何原型对象都有了一个isBad,这就足以说明其危险性,而Object.create(null)是没有__proto__的,即使强行赋一个__proto__的值,也不会改变其他对象的__proto__

当然在很多时候我们发现不少项目都有直接写{}的习惯,他们都有安全问题吗?当然不是,如果你能完全控制好自己的对象,不使其收到用户输入与环境的影响,那自然是没问题的,但上文提到的cache对象并不是这样,大致阅读下index.ts的代码就可以发现,外部可以给予它任何属性,没有做任何的检查,这当然是存在严重的问题的,使用y18n的程序只需要被注入一小段以__proto__为locale代码,系统很快就会奔溃掉。

关于使用Object.freeze防止原型链污染

其实除了用Object.create(null)创建对象以外,如果适当的时机使用Object.freeze()冻结__proto__,这个问题也不会发生

const foo = {
   }
const bar = {
   }
foo.progress//progress of this foo object
foo.__proto__ = Object.freeze(foo.__proto__)
foo.__proto__.isBad = true
//foo.__proto__.isBad is undefined

freeze掉__proto__之后再对__proto__进行修改时,不会产生任何效果,因为foo已经被冻结,此时即使对象暴露在外部,全局__proto__指针也是安全的,它事实上已经是只读的,但请注意只冻结对象是没有用的,Object.freeze无法冻结整个属性链,例如

const a = Object.freeze({
   b: {
   c: 1}})
a.b.c = 2

这样修改是成功的,因为Object.freeze只冻结了b属性,但b自己也是一个对象,它的属性c是不会被冻结的,在__proto__上也是一样的,

const foo = Object.freeze({
   })
const bar = {
   }
foo.__proto__.isBad = true
bar.isBad
//true

__proto__本身被冻结了,foo.__proto__ = xx是不会生效的,但是对__proto__中属性的操作仍然都是有效的

其他注意事项

  1. 不少情况下,Map都要比Object更好,应该经常注意是否具备用Map替代Object的情况,在2016年ES6兴起的时候,国外就有社区对这个问题进行了大量交流,也有很多大牛发文响应这一观点,近年来也有很多相关的中文文章
  2. 对外部JSON输入进行安全验证,例如使用jpv 一类的验证器,不过这些组件的安全问题也是值得关注的,例如CVE-2020-17479这样伪装成数组绕过验证的神奇漏洞
  3. 防止直接的代码注入,例如在web项目的console中警告用户不要输入未知代码,例如Facebook使用多国语言在console中警告用户停止输入未知内容的行为,在electron应用中应该在发布时关闭develop面板

FYI

本文写作于2021年4月2日并发布于lyrieek的掘金,于2023年7月16日进行修订发布于lyrieek的阿里云开发者社区。

目录
相关文章
|
2月前
|
开发者 图形学 iOS开发
掌握Unity的跨平台部署与发布秘籍,让你的游戏作品在多个平台上大放异彩——从基础设置到高级优化,深入解析一站式游戏开发解决方案的每一个细节,带你领略高效发布流程的魅力所在
【8月更文挑战第31天】跨平台游戏开发是当今游戏产业的热点,尤其在移动设备普及的背景下更为重要。作为领先的游戏开发引擎,Unity以其卓越的跨平台支持能力脱颖而出,能够将游戏轻松部署至iOS、Android、PC、Mac、Web及游戏主机等多个平台。本文通过杂文形式探讨Unity在各平台的部署与发布策略,并提供具体实例,涵盖项目设置、性能优化、打包流程及发布前准备等关键环节,助力开发者充分利用Unity的强大功能,实现多平台游戏开发。
54 0
|
2月前
|
JavaScript 前端开发 开发者
揭开JavaScript的神秘面纱:原型链背后隐藏的继承秘密
【8月更文挑战第23天】原型链是JavaScript面向对象编程的核心特性,它使对象能继承另一个对象的属性和方法。每个对象内部都有一个[[Prototype]]属性指向其原型对象,形成链式结构。访问对象属性时,若当前对象不存在该属性,则沿原型链向上查找。
27 0
|
4月前
|
设计模式 JavaScript 前端开发
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
JavaScript的继承机制基于原型链,它定义了对象属性和方法的查找规则。每个对象都有一个原型,通过原型链,对象能访问到构造函数原型上的方法。例如`Animal.prototype`上的`speak`方法可被`Animal`实例访问。原型链的尽头是`Object.prototype`,其`[[Prototype]]`为`null`。继承方式包括原型链继承(通过`Object.create`)、构造函数继承(使用`call`或`apply`)和组合继承(结合两者)。ES6的`class`语法是语法糖,但底层仍基于原型。继承选择应根据需求,理解原型链原理对JavaScript面向对象编程至关重要
97 7
【JavaScript】深入浅出JavaScript继承机制:解密原型、原型链与面向对象实战攻略
|
4月前
|
JavaScript
js奥义:原型与原型链(1)
js奥义:原型与原型链(1)
|
4月前
|
JavaScript 前端开发
JavaScript进阶-原型链与继承
【6月更文挑战第18天】JavaScript的原型链和继承是其面向对象编程的核心。每个对象都有一个指向原型的对象链,当查找属性时会沿着此链搜索。原型链可能导致污染、效率下降及构造函数与原型混淆的问题,应谨慎扩展原生原型、保持原型结构简洁并使用`Object.create`或ES6的`class`。继承方式包括原型链、构造函数、组合继承和ES6的Class继承,需避免循环引用、方法覆盖和不当的构造函数使用。通过代码示例展示了这两种继承形式,理解并有效利用这些机制能提升代码质量。
62 5
|
3月前
|
JavaScript C++
js【详解】原型 vs 原型链
js【详解】原型 vs 原型链
34 0
|
4月前
|
JavaScript 前端开发
JS原型链
JS原型链
26 0
|
5月前
|
前端开发 JavaScript
前端 js 经典:原型对象和原型链
前端 js 经典:原型对象和原型链
38 1
|
5月前
|
JavaScript 前端开发
前端 JS 经典:原型和原型链
前端 JS 经典:原型和原型链
42 0
|
5月前
|
JavaScript 前端开发
JavaScript 原型链继承:掌握面向对象的基础
JavaScript 原型链继承:掌握面向对象的基础