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 日核实。
如何修复?
- 在项目根目录运行
composer update cakephp/cakephp;如果使用独立数据库包,则运行composer update cakephp/database。在5.4.*、5.3.*或5.2.*约束下,这会分别升级到 5.4.4、5.3.8 或 5.2.16。 - 如果你的
composer.json固定了确切的受影响版本,请把它提升到你所在系列的修复版本,然后再次运行更新。 - 部署更新后的
vendor/目录。请按部署流程重载 Web PHP 工作进程或使 OPcache 缓存失效,确保缓存不会保留旧类。长时间运行的 PHP 进程,例如队列 worker 和以守护方式运行的命令,必须重启才能让旧代码退出内存。 - 在 4.6 系列上,用同样的命令更新到 4.6.6;它只解决了排序规则缺陷。
- 注意一个行为变化:包含空格的帧偏移量(例如区间字面量)现在会抛出
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, '"') . '"',因此一个嵌入的双引号会提前关闭标识符,其后的所有内容都逃出了预期的语句。
如何被利用?
公开的概念验证实验环境完整展示了两种机制:
- 攻击者找到应用转发给
range()、frame()或某个排序规则构造器的可访问请求参数。这就是全部前提条件;没有任何框架端点会自行暴露这些参数。 - 对窗口帧缺陷,构造的偏移量字符串被原样放进
RANGE BETWEEN与PRECEDING之间。研究人员的实验环境展示了该片段以未绑定、未加引号的形式出现在编译后的 SQL 中。这是在 SQLite 上仅编译通过的演示,没有演示读取数据表。 - 对排序规则缺陷,研究人员的 Postgres 实验环境执行了注入。对照值
collation=C编译为COLLATE "C"。负载C" AS text)) || (SELECT note FROM lab_secret LIMIT 1) --在公开报告中可见。它嵌入的引号从去除再包裹中逃出,尾部的注释吞掉了 Cake 的收尾文本。 - 注入的
SELECT返回一条见证行,实验环境报告读到的值为xCAKEPHP-COLLATE-SQLI-WITNESS,而被绑定的占位符仍然持有原始字面量。被绑定的第一个参数从来不是漏洞所在;第二个才是。
两个实验环境都在 GitHub 上,链接见参考文献,研究人员的报告将其描述为仅回环地址的复现。
攻击是如何流动的?
一句话:请求参数到达表达式构造器,该值被当作 SQL 片段拼接而不是被绑定,查询以应用的数据库权限执行。
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。
常见问题
默认的 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 日核实)