CodeQL 文档

原型污染赋值

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

单击查看 CodeQL 代码库中的查询

大多数 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);
});

参考资料

  • ©GitHub, Inc.
  • 条款
  • 隐私