我们的 AI 引擎 Neutron 在加州大学伯克利分校的 CyberGym 基准测试中取得了 97.1% 的成绩。 了解更多

深度解析

深度解析

CakePHP SQL注入,已在 5.4.4 中修复(CVE-2026-119418)

CakePHP 的查询表达式会拼接未校验的窗口帧偏移量和排序规则名,因此构造的输入可以注入 SQL;已在 5.4.4、5.3.8 和 5.2.16 中修复。

TL;DR

  • CakePHP 的数据库表达式对象中存在两个缺陷,它们把攻击者控制的字符串直接拼接进 SQL,而不是绑定它们。VulnCheck 于 2026 年 10 月 30 日将其发布为 CVE-2026-119418(窗口帧偏移量)和 CVE-2026-119419(排序规则名),并将发现归功于 Abraxas Labs 和 Mark Story。
  • 只有当应用自身的代码把请求数据传入受影响的参数时,应用才会暴露:通过 range() 或 frame() 传入的帧偏移量,或通过 StringExpression 和 IdentifierExpression 传入的排序规则名。默认的应用骨架不会在任何公开表单中暴露这两者。
  • 两个 CVE 记录均带有 CVSS v4.0 9.2 和 v3.1 8.1。目前没有已知的利用行为,且截至 2026 年 11 月 1 日,这两个 CVE 均不在 CISA 的 KEV 目录中。公开的概念验证实验环境自 2026 年 10 月 12 日起即可获取。
  • 升级到 CakePHP 5.4.4、5.3.8 或 5.2.16,三者均于 2026 年 11 月 1 日发布。CakePHP 4.6.6 只修复了排序规则缺陷;4.x 的处理路径和输入校验的临时方案见“如何修复?”。

我受影响吗?

当以下条件全部成立时,你处于暴露状态:

  • 你的应用运行 CakePHP 5.4.3 或更早版本,或一个没有修复版本的 5.x 或 4.x 版本,见下表。
  • 你自己的代码把一个由请求控制的值传入某个受影响的参数。这些参数包括 WindowExpression::range() 的 $start 和 $end、frame() 的偏移量或方向,以及传给 new StringExpression()、new IdentifierExpression() 或 setCollation() 的排序规则名。
  • 攻击者能够通过网络访问构建该查询的请求路径。

研究人员的受害者模型指出了两种现实的形态。其一是把“最近 N 天”输入转发给 range() 的分析报表生成器。其二是把区域设置或排序规则选择器转发给排序规则构造器的应用。官方的 cakephp/app 骨架不会转发任何一种,因此框架本身不会造成暴露。

分支 受影响版本 修复版本
CakePHP 5.4.x 5.4.0 至 5.4.3 5.4.4(2026 年 11 月 1 日)
CakePHP 5.3.x 5.3.0 至 5.3.7 5.3.8(2026 年 11 月 1 日)
CakePHP 5.2.x 5.2.0 至 5.2.15 5.2.16(2026 年 11 月 1 日)
CakePHP 5.1.x 及更早的 5.x 按 CVE 记录的范围全部受影响 未发布修复
CakePHP 4.6.x CVE-2026-119419 为 4.6.0 至 4.6.5;CVE-2026-119418 为 4.6.0 至 4.6.6 4.6.6(2026 年 11 月 1 日),仅排序规则缺陷
CakePHP 4.5.x 及更早的 4.x 全部 未发布修复

窗口帧缺陷只在表中的前三行得到修复。4.6.6 的源代码仍然接受原始字符串形式的帧偏移量,这一点已在其 WindowExpression.php 中核实;4.5 系列的最后一个版本 4.5.12(2026 年 8 月)早于两个修复。同一系列补丁还校验了 CVE 记录未描述的相邻参数:FunctionExpression 中的函数名、StringAggExpression 中的聚合排序字段,以及 partition() 和 orderBy() 的字符串。

先检查你运行的版本,再审计调用受影响 API 的代码:

# Framework version (the package the official skeleton requires)
composer show cakephp/cakephp | grep -E '^(name|versions)'
# If the database package was installed on its own
composer show cakephp/database 2>/dev/null | grep -E '^(name|versions)'
# Lock file
grep -A1 '"name": "cakephp/cakephp"' composer.lock | grep '"version"'

# Locate window APIs; inspect every argument and trace request data manually
grep -rnE '\->(range|frame)\b' src/ templates/ 2>/dev/null
# Collation names reaching a constructor or setter
grep -rnE 'setCollation\(\s*\$|new StringExpression\(|new IdentifierExpression\(' src/ templates/ 2>/dev/null

对每一条 grep 结果只需要回答一个问题:请求参数能否到达该实参?影响面很广。cakephp/cakephp 在 packagist.org 上的记录显示总安装量为 21,425,059,最近一个月为 557,686。承载这些类的拆分包 cakephp/database 显示总安装量为 29,353,847,最近一个月为 857,825。两个数字均于 2026 年 11 月 1 日核实。

如何修复?

  1. 在项目根目录运行 composer update cakephp/cakephp;如果使用独立数据库包,则运行 composer update cakephp/database。在 5.4.*、5.3.* 或 5.2.* 约束下,这会分别升级到 5.4.4、5.3.8 或 5.2.16。
  2. 如果你的 composer.json 固定了确切的受影响版本,请把它提升到你所在系列的修复版本,然后再次运行更新。
  3. 部署更新后的 vendor/ 目录。请按部署流程重载 Web PHP 工作进程或使 OPcache 缓存失效,确保缓存不会保留旧类。长时间运行的 PHP 进程,例如队列 worker 和以守护方式运行的命令,必须重启才能让旧代码退出内存。
  4. 在 4.6 系列上,用同样的命令更新到 4.6.6;它只解决了排序规则缺陷。
  5. 注意一个行为变化:包含空格的帧偏移量(例如区间字面量)现在会抛出 InvalidArgumentException。区间字面量的情况见常见问题。

如果你暂时无法升级,请在自己的调用边界上应用修复代码的规则:

  • 帧偏移量只传入整数、null 或可信的预先构建好的 ExpressionInterface 对象。
  • 自己把区间字面量包进 new QueryExpression("'1 day'")。
  • 直接调用 frame() 时,为帧类型和两个方向使用可信的框架常量,切勿将请求字符串传给这些参数。
  • 把排序规则名限制在应用自定义的允许列表内。

这个临时方案只覆盖你自己的调用点。任何用请求数据调用同一 API 的第三方代码仍然暴露,因此在升级排期期间,请把它当作针对两个 CVE 的权宜措施。

根本原因是什么?

这些表达式对象把构建器的若干参数当作 SQL 片段而非值来处理:绑定器存在于每个签名中,但从不绑定这些字符串。

CakePHP 的查询构建器区分被绑定的值和结构化 SQL,而它在 2026 年 8 月已经吸取过一次这个教训。CVE-2026-79752 给 FunctionsBuilder 的四个方法加上了 ensureSimpleString(),因为它们的参数是被拼接的片段。窗口帧偏移量和排序规则名不在那个名单上,Abraxas Labs 在修复之后的 2026 年 10 月 12 日排查中发现了这两处遗漏。

窗口帧缺陷位于 buildOffsetSql() 中,它组装 OVER 窗口的 BETWEEN 子句:

// src/Database/Expression/WindowExpression.php, before 5.4.4
protected function buildOffsetSql(
    ValueBinder $binder,
    ExpressionInterface|string|int|null $offset,
    string $direction,
): string {
    if ($offset === 0) {
        return 'CURRENT ROW';
    }

    if ($offset instanceof ExpressionInterface) {
        $offset = $offset->sql($binder);
    }

    return sprintf(
        '%s %s',
        $offset ?? 'UNBOUNDED',
        $direction,
    );
}

$binder 出现在签名里,但只有当偏移量已经是表达式对象时才起作用。一个普通字符串,包括来自 $request->getQuery('start') 的字符串,会经 sprintf() 拼进 OVER 子句内的 RANGE BETWEEN ... PRECEDING。

同族的 rows() 和 groups() 接受 ?int,无法携带这种情况。range() 接受字符串,是因为区间字面量的形态类似于 '1 day'。研究人员记录道,修复之前框架自己的测试就断言 range("'1 day'", "'10 days'")。

排序规则缺陷的形态相同,被绑定的值安全,第二个参数却是原始的:

// src/Database/Expression/StringExpression.php, before 5.4.4
public function sql(ValueBinder $binder): string
{
    $placeholder = $binder->placeholder('c');
    $binder->bind($placeholder, $this->string, 'string');

    return $placeholder . ' COLLATE ' . $this->collation;
}

字符串的值被绑定到占位符。排序规则则被直接拼在 COLLATE 之后,IdentifierExpression::sql() 也以同样方式拼接它。在 Postgres 上,据研究人员所述,驱动的标识符包裹方式是去除再包裹,即 '"' . trim($collation, '"') . '"',因此一个嵌入的双引号会提前关闭标识符,其后的所有内容都逃出了预期的语句。

如何被利用?

公开的概念验证实验环境完整展示了两种机制:

  1. 攻击者找到应用转发给 range()、frame() 或某个排序规则构造器的可访问请求参数。这就是全部前提条件;没有任何框架端点会自行暴露这些参数。
  2. 对窗口帧缺陷,构造的偏移量字符串被原样放进 RANGE BETWEEN 与 PRECEDING 之间。研究人员的实验环境展示了该片段以未绑定、未加引号的形式出现在编译后的 SQL 中。这是在 SQLite 上仅编译通过的演示,没有演示读取数据表。
  3. 对排序规则缺陷,研究人员的 Postgres 实验环境执行了注入。对照值 collation=C 编译为 COLLATE "C"。负载 C" AS text)) || (SELECT note FROM lab_secret LIMIT 1) -- 在公开报告中可见。它嵌入的引号从去除再包裹中逃出,尾部的注释吞掉了 Cake 的收尾文本。
  4. 注入的 SELECT 返回一条见证行,实验环境报告读到的值为 xCAKEPHP-COLLATE-SQLI-WITNESS,而被绑定的占位符仍然持有原始字面量。被绑定的第一个参数从来不是漏洞所在;第二个才是。

两个实验环境都在 GitHub 上,链接见参考文献,研究人员的报告将其描述为仅回环地址的复现。

攻击是如何流动的?

一句话:请求参数到达表达式构造器,该值被当作 SQL 片段拼接而不是被绑定,查询以应用的数据库权限执行。

flowchart LR A["请求参数到达
range()、frame() 或
排序规则构造器"] --> B["值以原始字符串形式
保存"] B --> C["ValueBinder 从不绑定
普通字符串
(缺失校验)"] C --> D["buildOffsetSql / sql()
把片段拼进
OVER 或 COLLATE"] D --> E["注入的 SQL 以
数据库连接的
权限执行"] E --> F["实验证实:读取数据表
通过 Postgres 排序规则
逃逸实现"]

攻击者能做什么?

  • 以应用数据库连接的权限运行 SQL。两个 CVE 记录都对被危害数据给出完全的机密性、完整性和可用性影响。
  • 读取数据。Postgres 上的排序规则实验演示了一个 SELECT,它返回了应用无意暴露的表中的一行。
  • 修改数据。研究人员将其归类为推断而非演示:同一注入以连接角色运行,因此写入是可能的,但实验只证明了读取。
  • 在畸形的输入上显式失败。无法解析的负载会在该请求上产生数据库错误,这也可能是一次探测尝试留下的痕迹。

攻击者需要的条件:一个把请求数据转发给受影响参数的应用、该路径的网络可达性,除此之外别无其他。框架层面没有作用于这些参数本身的认证关卡。受影响的端点是否经过认证完全取决于应用。这也是研究人员给这两个缺陷打 PR:L 分、而 CVE 记录以高攻击复杂度打 PR:N 分的原因。

为什么修复有效?

针对窗口族的提交 18de3e4c 和针对排序规则构造器的提交 ae8c354c 给每个受影响的实参加上了 QueryExpression::ensureSimpleString(),因此非简单字符串会在任何 SQL 构建之前抛出异常:

--- a/src/Database/Expression/WindowExpression.php
+++ b/src/Database/Expression/WindowExpression.php
@@ public function range(
     ExpressionInterface|string|int|null $start,
     ExpressionInterface|string|int|null $end = 0
 ) {
+    if (is_string($start)) {
+        QueryExpression::ensureSimpleString('start', $start);
+    }
+    if (is_string($end)) {
+        QueryExpression::ensureSimpleString('end', $end);
+    }
+
     return $this->frame(self::RANGE, $start, self::PRECEDING, $end, self::FOLLOWING);
 }
--- a/src/Database/Expression/StringExpression.php
+++ b/src/Database/Expression/StringExpression.php
@@ public function setCollation(string $collation): void
 {
+    QueryExpression::ensureSimpleString('collation', $collation);
     $this->collation = $collation;
 }
// src/Database/Expression/QueryExpression.php, 5.4.4
public static function ensureSimpleString(string $parameterName, string $value): void
{
    if (preg_match('/^(?:[\'"])?[a-zA-Z0-9_\.]+(?:[\'"])?$/', $value)) {
        return;
    }
    throw new InvalidArgumentException("Argument `{$parameterName}` must be an alphanumeric string");
}

这条规则只允许一个可选地带引号的、由字母数字、下划线和点组成的记号。非简单字符串(例如含空格的区间字面量)会抛出 InvalidArgumentException。该校验器使用字符允许列表,并非 SQL 关键字过滤器。需要结构的字符串,例如区间字面量,必须以调用者刻意构建的表达式对象传入。

该修复对 5.2、5.3 和 5.4 系列上的这些参数是完整的。补丁系列还校验了 FunctionExpression 构造器和 setName() 中的函数名、StringAggExpression::setAggregateOrderBy()、WindowExpression::partition() 和 orderBy(),以及两个排序规则设置器。对 4.x 系列它并不完整。4.6.6 版本只带有排序规则的移植修复,其 WindowExpression::range()、frame() 和 buildOffsetSql() 仍然接受并拼接原始字符串,这一点已在 4.6.6 的源代码中核实。

如何检查我是否已被入侵?

这两个缺陷没有公开的入侵指标,截至 2026 年 11 月 1 日也没有任何利用行为的报告。一次失败的注入尝试,除了受影响端点上的数据库语法错误之外,可能不会留下任何痕迹。

具体的审计就是上面的代码审查。grep 命令只是起点,并非完整审计:多行调用、别名和间接调用都需要手动检查。每一条结果都需要一行回答:请求数据能否到达该实参。

在数据库日志中,查找来自这些端点的语句里 COLLATE 子句或窗口 BETWEEN 子句包含引号、注释或意外关键字的情况。把命中当作需要调查的线索,而不是入侵的证据。

时间线

  • 2026-08-27:CakePHP 发布 CVE-2026-79752 的公告 GHSA-vjqc-q4mp-2rvf,为四个拼接片段参数的 FunctionsBuilder 方法加上 ensureSimpleString()。
  • 2026-10-12:Abraxas Labs 连同公开的概念验证实验环境发布两篇报告,各以认证前提的向量给出 8.1 High 的评分。
  • 2026-10-24:窗口族(拉取请求 19735)和排序规则构造器(拉取请求 19726)的修复被合并,并致谢 abraxas、SunKimsroul 和 Akinlabi。
  • 2026-10-25:4.x 的排序规则移植修复作为拉取请求 19734 编写完成。
  • 2026-10-30:VulnCheck 发布 CVE-2026-119418 和 CVE-2026-119419,各带 CVSS v4.0 9.2 和 v3.1 8.1。
  • 2026-11-01:CakePHP 发布包含两个修复的 5.4.4、5.3.8 和 5.2.16,以及只含排序规则修复的 4.6.6。

从公开概念验证到修复版本的 20 天:报告和实验环境于 10 月 12 日公开,修复于 10 月 24 日编写,CVE 记录于 10 月 30 日落地,修复版本于 2026 年 11 月 1 日发布。
图 1:从 Abraxas Labs 的公开报告到 CakePHP 修复版本的 20 天。来源:Abraxas Labs 的报告、cakephp/cakephp 的提交与标签、以及 CVE 记录,2026 年 11 月 1 日核实

常见问题

默认的 CakePHP 应用会暴露这些参数吗?

不会。据研究人员的核实,官方的 cakephp/app 骨架不会从任何公开表单转发窗口偏移量或排序规则名。暴露来自把请求数据传入这些表达式方法的应用代码,因此审计目标是你们自己的控制器、服务和报表生成器。

我的代码把像 '1 day' 这样的区间字面量传给 range()。5.4.4 会破坏它吗?

会,而且这个变化是有意的。包含空格的字符串无法通过 ensureSimpleString() 并抛出 InvalidArgumentException,因此请把区间字面量包进表达式对象:range(new QueryExpression("'1 day'"), new QueryExpression("'10 days'"))。框架自己的测试套件在 5.4.4 中也改成了完全相同的形式。

为什么 CVE 记录写 9.2,而研究人员写 8.1 High?

两者在 v3.1 分数 8.1 上一致,但对前提条件的建模不同。CVE 记录使用 PR:N、高攻击复杂度和一个攻击要求值,把应用的转发代码视为屏障。研究人员使用 PR:L 和低复杂度,对一个现实受害应用的已认证请求路径评分。两者描述的是同一个前提:应用必须把请求数据转发到受影响的参数。

CakePHP 4.x 受影响吗?

受影响,但只是部分。排序规则缺陷在 2026 年 11 月 1 日发布的 4.6.6 中修复。窗口帧缺陷在任何 4.x 系列中都没有修复版本:4.6.6 的 WindowExpression 仍然拼接原始字符串偏移量,而 4.5 系列的最后一个版本 4.5.12 早于两个修复。在 4.x 上把请求数据传入窗口帧的应用必须自行校验输入,具体见“如何修复?”。

composer audit 会标出这些 CVE 吗?

暂时不会。Composer 的 audit 命令读取 FriendsOfPHP 公告数据库,截至 2026 年 11 月 1 日该数据库尚未收录任何一个 CVE,因此本周可靠的检查是上面的版本查询。GitHub 公告数据库确实镜像了这两个记录,并归类为 critical,对应未复核条目 GHSA-h3r5-88vp-3qm7 和 GHSA-6q38-2qcr-3x34。

该缺陷正在被野外利用吗?

截至 2026 年 11 月 1 日,没有任何来源报告攻击,两个 CVE 都不在 CISA 的 KEV 目录中,这一点已对照该目录的 JSON 数据源核实。公开的产物是只与回环地址通信的实验复现,不是针对真实目标的攻击工具。

用 Ostorlab 在代码仓库中找出旧版本框架

Ostorlab 的软件成分分析会梳理你的代码仓库和容器镜像的依赖树。它列出每一个仍在使用 CakePHP 5.4.3 或更早版本的项目,以便在概念验证找到你的报表生成器之前,安排 5.4.4、5.3.8 或 5.2.16 的升级。

参考文献

  • CVE-2026-119418 记录,VulnCheck CNA,CVSS v4.0 9.2 和 v3.1 8.1,致谢 Abraxas Labs 和 Mark Story:https://www.cve.org/CVERecord?id=CVE-2026-119418(2026 年 11 月 1 日核实)
  • CVE-2026-119419 记录,VulnCheck CNA,CVSS v4.0 9.2 和 v3.1 8.1:https://www.cve.org/CVERecord?id=CVE-2026-119419(2026 年 11 月 1 日核实)
  • GitHub 公告数据库镜像,归类为 critical,未复核:https://github.com/advisories/GHSA-h3r5-88vp-3qm7 与 https://github.com/advisories/GHSA-6q38-2qcr-3x34(2026 年 11 月 1 日核实)
  • Abraxas Labs,“CakePHP, WindowExpression range/frame SQL injection”,2026 年 10 月 12 日:https://abraxaslabs.tech/research/cakephp-window-sqli(2026 年 11 月 1 日核实)
  • Abraxas Labs,“CakePHP, COLLATE collation SQL injection”,2026 年 10 月 12 日:https://abraxaslabs.tech/research/cakephp-collate-sqli(2026 年 11 月 1 日核实)
  • Abraxas Labs 的概念验证实验环境,自 2026 年 10 月 12 日起公开:https://github.com/abraxas/cakephp-window-sqli 与 https://github.com/abraxas/cakephp-collate-sqli(2026 年 11 月 1 日核实)
  • 窗口族修复提交 18de3e4cdabba049269421bf43c2ccd1a7451565,ADmad,编写于 2026 年 10 月 24 日,致谢 abraxas、SunKimsroul 和 Akinlabi:https://github.com/cakephp/cakephp/commit/18de3e4cdabba049269421bf43c2ccd1a7451565(2026 年 11 月 1 日核实)
  • 排序规则修复提交 ae8c354cf825aeb7090ea4b48ce98142446f3ac9,Mark Story,编写于 2026 年 10 月 24 日:https://github.com/cakephp/cakephp/commit/ae8c354cf825aeb7090ea4b48ce98142446f3ac9(2026 年 11 月 1 日核实)
  • 4.x 排序规则移植修复提交 21951faea40060dcc5cc1da3159056df66816d3c,编写于 2026 年 10 月 25 日:https://github.com/cakephp/cakephp/commit/21951faea40060dcc5cc1da3159056df66816d3c(2026 年 11 月 1 日核实)
  • CakePHP 5.4.4 发行说明,2026 年 11 月 1 日,写明 5.2.16 和 5.3.8 为另两个安全修复版本:https://github.com/cakephp/cakephp/releases/tag/5.4.4(2026 年 11 月 1 日核实)
  • 4.6.5 到 4.6.6 的对比,显示排序规则文件被修改且 WindowExpression 无改动:https://github.com/cakephp/cakephp/compare/4.6.5...4.6.6(2026 年 11 月 1 日核实)
  • 修复前的 WindowExpression,位于 CVE 记录引用的提交:https://github.com/cakephp/cakephp/blob/6f3dd91371ac0698b64024ddb21d57def9fbba91/src/Database/Expression/WindowExpression.php(2026 年 11 月 1 日核实)
  • 5.4.4 中修复后的校验器 ensureSimpleString():https://github.com/cakephp/cakephp/blob/5.4.4/src/Database/Expression/QueryExpression.php(2026 年 11 月 1 日核实)
  • 5.4.4 中把区间字面量包进 QueryExpression 的修复后测试套件:https://github.com/cakephp/cakephp/blob/5.4.4/tests/TestCase/Database/Expression/WindowExpressionTest.php(2026 年 11 月 1 日核实)
  • 前序公告 GHSA-vjqc-q4mp-2rvf,CVE-2026-79752,发布于 2026 年 8 月 27 日:https://github.com/cakephp/cakephp/security/advisories/GHSA-vjqc-q4mp-2rvf(2026 年 11 月 1 日核实)
  • cakephp/cakephp 的 Packagist 安装统计,总计 21,425,059,月度 557,686:https://packagist.org/packages/cakephp/cakephp(2026 年 11 月 1 日核实)
  • cakephp/database 的 Packagist 安装统计,总计 29,353,847,月度 857,825:https://packagist.org/packages/cakephp/database(2026 年 11 月 1 日核实)
  • composer audit 读取的 FriendsOfPHP 安全公告数据库,核实日期时尚未收录任何一个 CVE:https://github.com/FriendsOfPHP/security-advisories(2026 年 11 月 1 日核实)
  • CISA KEV 目录 JSON 数据源,于 2026 年 11 月 1 日核查两个 CVE(均未列入):https://www.cisa.gov/sites/default/files/feeds/known_exploited_vulnerabilities.json(2026 年 11 月 1 日核实)
  • 关闭时间戳校验时 PHP OPcache 的部署要求:https://www.php.net/manual/en/opcache.configuration.php#ini.opcache.validate-timestamps(2026 年 11 月 1 日核实)