原型污染赋值¶
ID: js/prototype-polluting-assignment
Kind: path-problem
Security severity: 6.1
Severity: warning
Precision: high
Tags:
- security
- external/cwe/cwe-078
- external/cwe/cwe-079
- external/cwe/cwe-094
- external/cwe/cwe-400
- external/cwe/cwe-471
- external/cwe/cwe-915
Query suites:
- javascript-code-scanning.qls
- javascript-security-extended.qls
- javascript-security-and-quality.qls
大多数 JavaScript 对象继承内置 Object.prototype
对象的属性。原型污染是一种漏洞,攻击者可以通过它修改 Object.prototype
。由于大多数对象继承自受损的 Object.prototype
对象,因此攻击者可以使用它来篡改应用程序逻辑,并经常升级为远程代码执行或跨站脚本攻击。
通过修改通过用户控制的属性名称获得的对象,可以造成原型污染。大多数对象都有一个特殊的 __proto__
属性,它引用 Object.prototype
。攻击者可以滥用此特殊属性来欺骗应用程序执行对 Object.prototype
的意外修改。
建议¶
使用对不受信任的键值具有弹性的关联数据结构,例如 Map。在某些情况下,使用 Object.create(null) 创建的无原型对象可能更可取。
或者,限制计算属性名称,使其不会与内置属性冲突,方法是使用常量字符串为其添加前缀,或者拒绝不符合预期格式的输入。
示例¶
在下面的示例中,不受信任的值 req.params.id
用作属性名称 req.session.todos[id]
。如果恶意用户传递 ID 值 __proto__
,则变量 items
将引用 Object.prototype
。最后,对 items
的修改使攻击者能够在 Object.prototype
上注入任意属性。
let express = require('express');
let app = express()
app.put('/todos/:id', (req, res) => {
let id = req.params.id;
let items = req.session.todos[id];
if (!items) {
items = req.session.todos[id] = {};
}
items[req.query.name] = req.query.text;
res.end(200);
});
解决此问题的一种方法是使用 Map 对象来关联键值对,而不是常规对象,如下所示
let express = require('express');
let app = express()
app.put('/todos/:id', (req, res) => {
let id = req.params.id;
let items = req.session.todos.get(id);
if (!items) {
items = new Map();
req.sessions.todos.set(id, items);
}
items.set(req.query.name, req.query.text);
res.end(200);
});
解决此问题的另一种方法是阻止 __proto__
属性用作键,如下所示
let express = require('express');
let app = express()
app.put('/todos/:id', (req, res) => {
let id = req.params.id;
if (id === '__proto__' || id === 'constructor' || id === 'prototype') {
res.end(403);
return;
}
let items = req.session.todos[id];
if (!items) {
items = req.session.todos[id] = {};
}
items[req.query.name] = req.query.text;
res.end(200);
});