编程 PHP 8.5.12 升级笔记:两个未授权 RCE 的缓解配置,以及几条会反噬的“土办法”

2026-11-12 20:01:24

PHP 8.5.12 升级笔记:两个未授权 RCE 的缓解配置,以及几条会反噬的“土办法”

信源:PHP 官方新闻存档(2026)

PHP 8.5.12、8.4.27、8.3.36、8.2.35 修复了两个可导致未授权远程代码执行的严重漏洞,CVSS v4.0 评分均为 9.2。所有对外提供 Web 服务的 PHP 安装都受影响,包括 PHP-FPM、Apache + mod_php、php-cgi、FrankenPHP 和 LiteSpeed。官方明确表示 mod_php 风险最高:已有利用演示显示,未授权请求可以在任何应用代码执行之前触发攻击。

两个 RCE 的机理

CVE-2026-109618(GHSA-w8hw-w9fx-j79w):OOM 中断导致的半初始化数据被当作有效数据

内存耗尽(out-of-memory)的处理过程可能在内部请求数据只初始化了一部分时打断 PHP。随后的请求清理逻辑会把这段无效数据当成有效数据处理。

CVE-2026-109619(GHSA-6wmf-4rv5-p3rp):残留的全局状态

请求私有的内部状态可以在其底层内存已被释放之后继续存活。同一个 worker 处理的后续请求,可能在正常初始化重置它之前就用上这份陈旧状态。

这正是 mod_php 更危险的原因:进程模型里 worker 长期驻留、请求之间共享地址空间,陈旧状态存活窗口更长;而 mod_php 又在请求进入应用路由之前就暴露在解析路径上。

无法立刻升级时的缓解

只有升级是完整方案。以下措施只削减特定攻击路径的暴露面,不会让安装变安全。

CVE-2026-109618 相关

1)把 memory_limit 设为 -1,并确保应用改不回去

优先用 FPM pool 的 php_admin_value 或 Apache 配置下发,而不是 php.ini 或应用层设置:

; php-fpm pool 配置
php_admin_value[memory_limit] = -1
# Apache + mod_php
php_admin_value memory_limit -1

设完之后要逐个 pool、逐个目录、逐个应用配置确认没有地方把有限值写回来。这只消除了“可控的 PHP 内存上限失败”,底层分配失败仍然可能走到漏洞路径上。

2)压低 post_max_size

不需要文件上传的应用设 post_max_size = 8M;需要上传的设 post_max_size = 32M,并保持 upload_max_filesize 严格低于 post_max_size:

; 无上传场景
post_max_size = 8M

; 有上传场景
post_max_size = 32M
; upload_max_filesize 必须小于 post_max_size

3)在 Web 服务器或代理层加限制

针对解码后的请求体大小、输入字段数量、multipart part 数量、header 数量、嵌套深度分别设限。这些限制必须同时覆盖没有可用 Content-Length 的请求,否则绕过成本很低。

4)容器 / cgroup 只当可用性边界

用 cgroup 内存限制兜住最坏情况,策略上倾向于只杀掉受影响的 worker,而不是整组进程。不要用 RLIMIT_AS 或 RLIMIT_DATA 替代——这两个限制恰恰会制造出漏洞所依赖的那种分配失败,属于反向操作。

CVE-2026-109619 相关

1)关闭 session upload progress

; php.ini
session.upload_progress.enabled = 0
; php-fpm pool
php_admin_value[session.upload_progress.enabled] = 0

改完必须重启每一个 worker。这个值必须写在请求解析之前就生效的位置:php.ini、FPM pool 或服务器配置。不要依赖 ini_set() 或 FPM 的 .user.ini——它们生效太晚。该开关只挡住已确认的 session 路径,挡不住其他变体。

2)PHP 8.5 上关闭 fatal error backtrace

; php.ini
fatal_error_backtraces = 0

同样要重启所有 worker。它只挡住已确认的 fatal backtrace 路径。

3)清掉每请求应用的无效 / 废弃设置

这些设置会在初始化之前产生诊断输出。用 error_reporting、display_errors 或 @ 把诊断藏起来没用,问题不在显示层。

4)修掉攻击者可达的 fatal error 与执行超时

包括 callback、上传或导入处理、清理阶段里的失败。

后续 CVE-2026-115560:一个很窄的触发组合

单独发公告的 session upload progress 问题,触发条件必须同时满足:

  • 应用在配置里把 session.serialize_handler 设为 php_binary 或 php_serialize;
  • 运行期用 ini_set() 把它改成 php,然后才调用 session_start();
  • session upload progress 仍然开启。

不在运行期改 serialize handler 的应用可以继续开着这个功能。官方认为这种配置组合现实中很少见,因此走常规发布周期,会在下一个版本随 CVE-2026-115560 的完整公告一起发布。

Windows 构建:Brotli 的 CVE-2026-111497 与 -1 后缀

8.5.12-1、8.4.27-1、8.3.36-1、8.2.35-1 内置了 Brotli 1.2.1,修复 CVE-2026-111497——Brotli 解压中的越界写。Windows 构建里,开启自动 HTTP 响应解压时可以通过 cURL 扩展到达这个解码器;cURL 默认关闭该行为,但应用和 HTTP 客户端库可以主动打开。

-1 后缀只表示这是依赖更新后的 Windows 包重建,PHP 运行时版本本身没变。

为什么建议直接跳到最新补丁版

同一批工作的 8.5.11(2026-09-24)已经修了一串问题,节选自 ChangeLog-8:

  • FPM:修复 GH-19320(FPM UID / GID 溢出);修复 GHSA-62xp-839h-2637(listen.allowed_clients 的 IPv6 ACL 绕过,原因是地址比较不完整),CVE-2026-91768。
  • OpenSSL:修复 GHSA-vvx9-73fr-5jjx(SAN 不匹配后 TLS 主机名校验回退到 CN),CVE-2026-91769;修复 GHSA-xr7j-rvgx-xq5p(构造的服务器证书通配符 CN 触发 php_openssl_matches_wildcard_name() 堆缓冲区溢出),CVE-2026-91767。
  • Phar:修复 GHSA-j3wh-g957-2m85(phar_tar_number() 整数溢出,可导致 TAR 归档条目注入),CVE-2026-6103。
  • MySQLnd:修复 GHSA-r6x9-5r99-36j7(mysqlnd 线协议中的多处报文越界读),CVE-2025-1218。
  • Standard:修复 GHSA-fpwc-w8rq-cr92(HTTP stream wrapper 重定向中的跨源凭据泄漏),CVE-2026-91766;修复 GHSA-7875-c8px-7q5f(跟随空 Location 头重定向时 HTTP stream wrapper 越界读),CVE-2026-93682。

8.5.12 又叠加了两个 RCE 和 Windows 的 Brotli 重建。逐个 cherry-pick 的代价是容易漏项,而且漏掉的往往是 ACL 绕过、证书校验回退这类不报错、只影响边界判断的问题。直接换到最新补丁版更省事。

8.2 EOL 与 8.6 时间线

PHP 8.2 于 2026-12-31 结束生命周期,此后不再接收任何安全修复。PHP 8.1 及更早已停止更新;鉴于本次问题严重性,PHP 安全团队为 PHP 7.4、8.0、8.1 提供了最小化的源码 backport,供自行编译或无 LTS 厂商支持的用户使用。

PHP 8.6.0 计划于 2026-11-19 发布首个 GA,8.6.0 RC4 也已包含这两个 RCE 的修复。

升级后验证

# 1. 确认运行时版本
php -v

# 2. 确认缓解项实际生效值(通过 FPM 跑一次,而不是 CLI 直读)
php -i | grep -E 'memory_limit|post_max_size|upload_max_filesize|session.upload_progress.enabled|fatal_error_backtraces'

# 3. 校验 FPM 配置语法与 pool 加载
php-fpm -tt

再补两步人工确认:

  • 打开 pool 配置文件,确认 memory_limit、session.upload_progress.enabled 写在 php_admin_value[...] 里,而不是靠 .user.ini 或应用内 ini_set() 下发;
  • 用一次真实 FPM 请求读取 ini_get(),比对第 2 步的值。CLI 和 FPM 的 ini 是两套,php -i 在 CLI 下看到的不是线上生效值。
复制全文 生成海报 PHP 安全 运维 PHP-FPM CVE

推荐文章

程序员茄子在线接单