javascript - 如何防止 JavaScript 中的原型(prototype)污染

标签 javascript node.js security

最近我偶然发现了一个vulnerability in doT.js 。该漏洞的存在是因为攻击者可以利用原型(prototype)污染来修改传入 doT 的选项值。

示例:

var doT = require("dot");
var tempFn = doT.template("<h1>Here is a sample template " +
    "{{=console.log(23)}}</h1>");
tempFn({})
var doT = require("dot"); // prototype pollution attack vector
Object.prototype.templateSettings = {varname:"a,b,c,d,x=console.log(25)"};
// benign looking template compilation + application
var dots = require("dot").process({path: "./resources"});
dots.mytemplate();

然后我开始思考:这是否意味着几乎任何 JavaScript 库的 API 选项都可能因原型(prototype)污染而受到损害?

例如,这里的 express.static 与选项一起使用。

var options = {
  dotfiles: 'ignore',
  etag: false,
  extensions: ['htm', 'html'],
  index: false,
  maxAge: '1d',
  redirect: false,
  setHeaders: function (res, path, stat) {
    res.set('x-timestamp', Date.now())
  }
}

app.use(express.static('public', options))

攻击者难道不能设置Object.prototype.redirect = true,并且如果用户未指定,就会发生重定向吗?而且肯定还有更多的恶意用例。

作为库作者,可以做些什么来允许传递选项但防止原型(prototype)污染?

编辑:我特别关注使用 NPM 分发的包。例如,doT.js 的作者可以采取什么措施来解决该漏洞?

最佳答案

Olivier Arteau发布了完整的 PDF 白皮书 Prototype pollution attack in NodeJS application其中包括识别和缓解攻击。

<小时/>

攻击的一般概念

原型(prototype)污染背后的一般思想始于攻击者至少可以控制以下形式的任何表达式的参数avalue:

obj[a][b] = value;

攻击者可以将a设置为__proto__,并且具有由b定义的名称的属性将在所有现有对象(该对象的应用程序的 obj 的类),其值为 value

当攻击者至少控制abvalue时,同样的东西可以附加以下形式。

obj[a][b][c] = value;

攻击者可以将a设置为constructor,将b设置为prototype,并将属性名称定义为c 将在应用程序的所有现有对象上定义,值为 value

但是,由于这需要更复杂的对象分配,因此第一种形式更容易使用。

虽然您很少会偶然发现与所提供的示例文本类似的代码,但一些操作可以为攻击者提供类似的控制。

缓解措施

使用映射而不是对象

它本质上作为一个 HashMap 工作,但没有 Object 所具有的所有安全警告。当需要键/值结构时,Map 应优先于 Object

Object.create(null)

可以在 JavaScript 中创建没有任何原型(prototype)的对象。它需要使用Object.create函数。通过此 API 创建的对象不会具有 __proto__constructor 属性。以这种方式创建对象可以帮助减轻 原型(prototype)污染攻击。

let obj = Object.create(null);
obj.__proto__ // undefined
obj.constructor // undefined

JSON 输入的架构验证

npm 上的多个库(例如: ajv )为 JSON 数据提供架构验证。架构验证确保 JSON 数据包含具有适当类型的所有预期属性。当使用这种方法来减轻“原型(prototype)污染”攻击时,拒绝不需要的属性非常重要。在 ajv ,这可以通过在架构上将 additionalProperties 设置为 false 来完成。

卡住原型(prototype)

使用Object.freeze将缓解几乎所有可利用的情况。

请注意,虽然向基础对象的原型(prototype)添加函数是一种令人不悦的做法,但它仍然可以在 Node.js 应用程序或其依赖项中使用。强烈建议在走这条路线之前检查您的 Node.js 应用程序及其依赖关系以了解此类用法。由于卡住对象的行为是在属性分配上默默失败,因此可能会引入难以识别的错误。

Object.freeze(Object.prototype);
Object.freeze(Object);
({}).__proto__.test = 123;
({}).test // this will be undefined

关于javascript - 如何防止 JavaScript 中的原型(prototype)污染,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57780961/

相关文章:

javascript - 鼠标左键单击与 JavaScript 中的右键单击例程发生冲突

javascript - Node4 使用 gulp 支持 TypeScript 到 EcmaScript2015

node.js - 如何创建 GitHub 操作以使用 Yarn 运行 Jest 测试?

node.js - ## Node 纤维有问题##

ruby-on-rails - 寻找在 Ruby on Rails 中构建安全 REST API 的建议

javascript - php服务器无法使用$.http().post请求从angularjs接收json变量

javascript - 将原型(prototype)添加到 JSON 字符串中的所有对象

python - python 中的评估安全性,从 JSON 中提取字符串

php - CSRF token 和随机数中的熵

javascript - 将 .val() 转换为字符串 jQuery