Ben*_*ler 1 javascript security node.js
最近我偶然发现了doT.js 中的一个漏洞。该漏洞的存在是因为攻击者可以使用原型污染来修改传递给 doT 的选项的值。
例子:
var doT = require("dot");
var tempFn = doT.template("<h1>Here is a sample template " +
"{{=console.log(23)}}</h1>");
tempFn({})
Run Code Online (Sandbox Code Playgroud)
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();
Run Code Online (Sandbox Code Playgroud)
然后我开始思考:这是否意味着几乎所有JavaScript 库的 API 选项都可能因原型污染而受到损害?
例如,hereexpress.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))
Run Code Online (Sandbox Code Playgroud)
攻击者不能设置Object.prototype.redirect = true,如果用户未指定,会发生重定向吗?肯定还有更多恶意用例。
作为库作者,可以做些什么来允许传递选项但防止原型污染?
编辑:我特别关注与 NPM 一起分发的包。例如,doT.js 的作者可以做些什么来解决这个漏洞?
Olivier Arteau发布了一份完整的白皮书 PDF,称为NodeJS应用程序中的Prototype 污染攻击,涵盖了攻击的识别和缓解。
原型污染背后的一般思想始于这样一个事实:攻击者至少可以控制参数a和value以下形式的任何表达式:
obj[a][b] = value;
Run Code Online (Sandbox Code Playgroud)
攻击者可以设置a为__proto__并且名称定义为 的属性b将obj在应用程序的所有现有对象(属于 的类)上定义为 值value。
当攻击者至少控制a,b和时,同样的事情可以附加以下形式value。
obj[a][b][c] = value;
Run Code Online (Sandbox Code Playgroud)
攻击者可以设置a为 constructor ,btoprototype和定义名称的属性c将在应用程序的所有现有对象上定义为值value。
然而,因为这需要更复杂的对象分配,所以第一种形式更容易使用。
虽然您很少会偶然发现在文本上看起来像提供的示例的代码,但一些操作可以为攻击者提供类似的控制。
它本质上作为 HashMap 工作,但没有所有的安全警告Object。当需要键/值结构时,Map应该优先于Object.
可以在 JavaScript 中创建没有任何原型的对象。它需要使用该Object.create函数。通过此 API 创建的对象将不具有__proto__和 constructor 属性。以这种方式创建对象有助于减轻原型污染攻击。
let obj = Object.create(null);
obj.__proto__ // undefined
obj.constructor // undefined
Run Code Online (Sandbox Code Playgroud)
npm 上的多个库(例如:ajv)为 JSON 数据提供模式验证。架构验证确保 JSON 数据包含具有适当类型的所有预期属性。当使用这种方法来减轻“原型污染”攻击时,重要的是拒绝不需要的属性。在ajv 中,这可以通过在架构上设置additionalProperties为来完成false。
使用Object.freeze将减轻几乎所有可利用的情况。
请注意,虽然向基础对象的原型添加函数在实践中是令人不悦的,但它仍可能在您的 Node.js 应用程序或其依赖项中使用。强烈建议在沿着这条路线前进之前检查您的 Node.js 应用程序及其对此类使用的依赖。由于冻结对象的行为是在属性分配时静默失败,因此可能会引入难以识别的错误。
Object.freeze(Object.prototype);
Object.freeze(Object);
({}).__proto__.test = 123;
({}).test // this will be undefined
Run Code Online (Sandbox Code Playgroud)
作为库作者,可以做些什么来允许传递选项但防止原型污染?
您可以使用 来检测某个属性是在您的实际对象上还是通过原型继承.hasOwnProperty()。但是,攻击者.hasOwnProperty()也可以覆盖并改变其行为。
正如我在评论中所说,在 Javascript 程序中使用您的库的人可以访问您的代码的完整源代码。因此,他们甚至不必使用原型污染来修改东西——他们可以随心所欲地修改你的代码。
为了完全保护您的代码,您必须仅分发在不同进程中运行并具有进程间 API(例如 http 服务器)的已编译可执行文件,或者您必须将代码放入服务中并仅提供以这种方式访问。如果您要分发 Javascript 库,就其本质而言,您必须分发源代码,以便使用您的库的程序员可以真正对其进行任何操作。他们甚至不必诉诸原型技巧。
| 归档时间: |
|
| 查看次数: |
2426 次 |
| 最近记录: |