2026 年 10 月 21 日,大概是最近几年 PHP 生态最忙的一天:两个 CVSS 9.2 的未认证远程代码执行漏洞(CVE-2026-109618 / CVE-2026-109619)在补丁发布前后,全球的运维、主机商、面板厂商几乎都在连夜升级。
官方给这组漏洞起了个名字:PHP2Shell。这篇不重复「怎么查版本」——那一步其实很短:php -v,低于 8.4.27 / 8.3.36 / 8.2.35 / 8.5.12 就说明没打补丁。本文要讲的是另外三件事:官方消息源时间线、不能立刻升级时的缓解措施(附官方原文与实测注解)、以及这些措施到底能挡多少。
先看一句官方原话,这句话定义了整篇文章的基调:
「only an update is a complete solution」 —— 只有升级才是完整解决方案;下面的措施只是降低特定攻击路径的暴露,并不能让安装变安全。
1、为什么这次全球都在熬夜
| 维度 | 情况 |
|---|---|
| 严重程度 | CVSS v4.0 = 9.2,无需任何认证 |
| 影响层面 | PHP 引擎本身,不是某个应用/插件的漏洞 |
| 受影响面 | 所有「处理 Web 请求」的 PHP:FPM、mod_php、php-cgi、FrankenPHP、LiteSpeed |
| 最危险的情况 | mod_php:官方说有演示利用,在任何应用代码执行之前就能触发 |
| 利用门槛 | 官方措辞是「没有简单的利用方法」,但 watchTowr 蜜罐在补丁发布前后已看到在野尝试 |
换句话说:它不挑目标,也不看你的站有没有装什么插件 —— 只要你的 PHP 在处理外部请求,就在射程内。
2、官方消息源与时间线

这一节是本文最想给你留档的部分,全部给可点开的官方来源:
| 时间(北京时间) | 事件 | 来源 |
|---|---|---|
| 10-20 前后 | 漏洞细节与补丁计划提前泄露,安全圈开始预警 | Sansec / watchTowr 等 |
| 10-21 03:02 | Cloudflare 对所有 WAF 用户(含免费版)默认启用防护,比 PHP 官方发布早了约 9 小时 | Cloudflare 官方博文 |
| 10-21 14:00 UTC (22:00 北京) |
PHP 官方发布 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 | php.net 安全公告 |
| 同一时间 | watchTowr 蜜罐观测到首批在野利用尝试;Cloudflare CTO 确认「早期攻击已经出现」 | SecurityWeek |
| 同一时间 | 官方额外提供 PHP 7.4 / 8.0 / 8.1 的源码 backport(自己编译的人可用) | php.net 公告 |
| 10-21 22:15 | 本站服务器完成升级:8.4.26 → 8.4.27,全程中断不到 2 秒 | 本站实测 |
两个技术细节值得单独说:
- CVE-2026-109618(OOM 腐败):内存耗尽时的处理逻辑会中断 PHP,此时请求内部数据只初始化了一半,清理阶段把无效数据当成有效数据处理 → RCE。
- CVE-2026-109619(全局状态残留):请求私有状态在内存释放后仍然存活,同一 worker 处理的下一个请求可能用到这份陈旧状态 → RCE。
这两个机制决定了后面的缓解措施为什么长那样 —— 它们都是对着「触发条件」打补丁,而不是修根因。
3、不能立刻升级时:官方缓解措施(含实测注解)

以下内容照抄 php.net 官方公告,我只补了中文解释和「坑在哪」。
① 针对 CVE-2026-109618(内存超限)
| 措施 | 官方要求 | ⚠️ 坑在哪 |
|---|---|---|
| 关闭 PHP 内存上限 | memory_limit = -1,用应用无法覆盖的方式设置(FPM pool 的 php_admin_value 或 Apache 配置),并确认没有任何 pool / 目录 / 应用级配置又把它设回有限值 |
不留限制 = 单请求可能吃光内存 ✗ → 必须配合 cgroup(见下) |
| 限制请求体大小 | 无上传 post_max_size = 8M;有上传 32M;并保证 upload_max_filesize < post_max_size |
单独设 8M 不够 ✗:有实验记录显示 3MB 请求体就打爆了 128M 上限 |
| 前置层限制请求结构 | 在 Nginx / WAF 限制:解码后的请求大小、字段数量、multipart 段数、请求头数量、嵌套深度 | 这些限制必须也覆盖没有 Content-Length 的请求 ✓(chunked 传输绕过很常见) |
| 容器 / cgroup 限制 | 用 cgroup 做可用性边界,最好只杀掉出问题的那个 worker | 绝对不要用 RLIMIT_AS / RLIMIT_DATA ✗ —— 它们会触发同一个漏洞路径 |
② 针对 CVE-2026-109619(状态残留)
| 措施 | 官方要求 | ⚠️ 坑在哪 |
|---|---|---|
| 关上传进度 | session.upload_progress.enabled = 0,并重启所有 worker |
必须写在 php.ini / FPM pool / 服务器配置里,不能用 ini_set() 或 .user.ini ✗(那时请求解析已经开始) |
| 仅 PHP 8.5 | fatal_error_backtraces = 0,重启 worker |
只挡「已确认的 fatal backtrace 路径」,其他变体不挡 |
| 清理废弃配置 | 移除那些会在初始化之前就产生诊断信息的、按请求应用的无效/废弃设置 | 用 error_reporting / display_errors / @ 掩盖是无效的 ✗ |
| 修根因 | 修掉攻击者可达的 fatal error 与执行超时(回调、上传/导入处理、清理逻辑里都算) | 这一条最容易被忽略,但它是唯一治本的 |
⚠️ 三个必须纠正的常见误解
- 「把 memory_limit 调低更安全」 ✗ —— 官方要的是 设为 -1,因为漏洞触发点正是「撞到内存上限后的处理逻辑」。调低等于主动帮攻击者更快触发。这是中文圈流传最广的错误操作。
- 「关掉
session.upload_progress就安全了」 ✗ —— 官方明确写的是「blocks the confirmed session paths but not other variants」:只挡已确认的路径,不挡其他变体。 - 「设了 8M 就够了」 ✗ —— 8M 是 PHP 默认值,单靠它挡不住,必须叠加前置层限制 + cgroup。
4、这些措施的效果边界

把它想成三道闸门,而不是一道:
| 闸门 | 能挡什么 | 挡不住什么 |
|---|---|---|
| Cloudflare WAF(已完成 ✓) | 已知的利用特征;免费版也默认开启 | 新变种;绕过 CF 直连源站的请求 ✗ |
| PHP 缓解配置(本文措施) | 已确认的触发路径(OOM 处理、session 路径) | 未公开的变体;它只是降低概率,不是修复 |
| cgroup | 内存被打爆时保机器不宕 | 被打中的那个 worker / 那台容器仍然会挂 |
所以正确的组合是:CF 挡特征 + PHP 缓解收窄路径 + cgroup 兜底存活 + 尽快升级 —— 前三个是买时间,最后一个才是解决。
5、正确的做法:升级(实测 20 秒)
# Debian / Ubuntu(发行版包)
apt update
apt list --upgradable | grep php
apt-get install --only-upgrade 'php8.4-*'
# 重启 PHP 与 Web 服务
systemctl restart php8.4-fpm && systemctl reload nginx
# 验证
php -v
本站实测:从检查到升级完成 约 20 秒,fpm 重启造成的中断 不到 2 秒,nginx 全程正常响应。所以「来不及升级」这个前提,多数情况下其实不成立。
几个升级坑:
- PHP 8.1 及以下:官方不再维护,只能找发行版 / LTS 厂商要 backport,或者直接升到支持的版本;
- remi 源用户:官方公告明确说该仓库当天不会有更新 ✗,建议切到发行版包或自己编译;
- Docker:换镜像 +
docker compose up -d重建容器,不是restart(restart 只重启进程,镜像还是旧的),重建后进容器php -v确认; - 升完记得 重启 worker / 容器 ✓ —— 装完不重启,跑的还是旧二进制。
附:PHP 7.4 / 8.0 / 8.1 也有源码 backport(但普通用户用不上)
这是这次事件里最有意思的一个细节:官方公告一边写着「PHP 8.1 及更早版本不再获得更新」✗,一边又在 GitHub 上给这三个版本开了针对本次漏洞的补丁分支 ✓ —— 严格说是「不再发新版本」,而不是「完全不管」。
| 补丁分支 | 最新提交 | 提交说明 | 日期 |
|---|---|---|---|
| PHP-8.1-CVE-2026-109618-109619 | 1d7dec1 |
Avoid state corruption via bailout | 2026-10-18 |
| PHP-8.0-CVE-2026-109618-109619 | 82f8f31 |
Avoid state corruption via bailout | 2026-10-18 |
| PHP-7.4-CVE-2026-109618-109619 | 712e60f |
Avoid state corruption via bailout | 2026-10-18 |
三个分支都只包含一个针对性提交,提交说明完全一致(「Avoid state corruption via bailout」,避开 bailout 导致的状态损坏);提交日期是 10 月 18 日 —— 比公开披露早了三天,说明官方在发公告之前就已经准备好了。
⚠️ 但它不是给普通用户准备的:分支里只有源码,没有二进制包。要用它得走完整流程:
git clone https://github.com/php/php-src.git
cd php-src
git checkout PHP-8.1-CVE-2026-109618-109619
./buildconf --force
./configure --prefix=/usr/local/php-8.1-patched [换成你自己的编译参数]
make -j$(nproc) && make install
官方对它的定位写得很清楚:「帮助那些从源码构建、或没有 LTS 厂商支持的人」 —— 不是让你在生产机上临时编译玩。
给普通用户的建议:
- 优先用发行版包 ✓(Debian / Ubuntu / RHEL)—— 厂商会做 backport,比如 Debian 13 直接推了 8.4.27;
- remi 源用户 ✗ —— 官方公告明确说该仓库当天不会有更新,建议切到发行版包或源码编译;
- 不要为了省事用旧版本源码补丁硬撑 ✗ —— 8.1 及以下的其他安全问题不会再修,这个补丁只解决这两个 CVE;
- 8.2 用户也要留意 ✓:官方公告写明 PHP 8.2 将于 2026-12-31 结束支持,到时候连 backport 都不会有。
6、我们这台机器的实际处置

| 层级 | 做了什么 | 验证结果 |
|---|---|---|
| 边缘 | Cloudflare 托管 WAF(7 条 Anomalous Execution 规则)+ 自定义敏感路径拦截 + 速率限制 | 规则均 block 且启用 ✓ |
| 源站入口 | 只允许 Cloudflare 回源,其余直连一律 403 | 直连返回 403 ✓,走 CF 返回 200 ✓ |
| PHP | 8.4.26 → 8.4.27 | CLI / FPM / OPcache 全部 8.4.27 ✓ |
| 后续 | 每日 webshell 扫描(基线 0)+ 每 10 分钟自动检查补丁 + 备份留档 | 扫描正常 ✓,自动任务已修掉「不刷新 apt 源」的坑 ✓ |
顺带说一个我自己踩的坑:自动补丁脚本只查 apt 的本地缓存,从不 apt-get update ✗ —— 等于永远不会发现新版本。这类「看起来在自动、其实没在动」的脚本,比没有更危险。
7、该慌吗
不用慌,但也不能拖。
- 官方说「没有简单的利用方法」→ 说明目前不是「一条 curl 就打穿」的水平;
- 但在野尝试已经出现,补丁也放了,逆向分析是迟早的事;
- 所以判断标准很简单:能升级就升级(20 秒的事);实在不能升,就按第 3 节把路径收窄,并且把升级排进当天日程,而不是"以后再说"。
8、官方来源汇总
- PHP 官方安全公告(含本轮缓解措施原文):php.net — Security advisory: Critical RCE, update now
- 漏洞详情:GHSA-w8hw-w9fx-j79w(109618)、GHSA-6wmf-4rv5-p3rp(109619)
- Cloudflare 默认防护与时间点:Cloudflare 官方博文
- 在野利用观测:SecurityWeek、Dark Reading
- 缓解措施的实测记录与边界说明:php2shell-response-kit(记录了「8M 单独不够」的实验)
- 中文风险通告分析(指出「调低 memory_limit 是反向操作」):分析文章
(本文技术内容以 php.net 官方公告为准;其余厂商措施仅作补充参考,实施前请自行核对官方原文。)

F12笔记






