下午两点十二分,手机震了一下。
我正干着别的事,随手瞟了一眼,看到开头四个字是「【阿里云】」,后面跟着「安全风险通告」。当时心里就是咯噔一下——这条短信不是营销,阿里云的安全通知从来不发废话。

短信内容很短:
【阿里云】尊敬的 aliyun*33711: 【安全风险通告】PHP 曝出"php2shell"远程代码执行漏洞(CVE-2026-109618、CVE-2026-109619)。如您的业务使用 PHP,请尽快自查并升级至安全版本,避免服务器遭受攻击。
PHP。远程代码执行。
那一瞬间我脑子里过的画面是:博客被人挂上一句话木马、首页被改成黑底红字的乱码、服务器在替我挖矿、账单开始飙……
虽然我盯着"如您的业务使用 PHP"这几个字看了两遍,但说实话,第一反应还是——我这站,到底是不是 PHP 的?
先别慌,我的站到底是什么
写博客这件事有个坏处:搭完之后就很少再想底层细节了。我确实记得自己是拿 Hugo 弄的,但"Hugo 是什么"和"我的服务器上装了什么"是两件事,中间隔着 Nginx、宝塔、几个自己加的小服务。人一紧张,这些细节就全糊在一起了。
所以我干脆不靠记忆,直接上服务器查。
| |
三条命令下去,答案很干净:零个 PHP 文件,零条 fastcgi 配置,机器上没装 PHP。
心跳恢复正常。
再回头审视一遍自己的架构,其实一目了然:
| |
整条链路上,PHP 出现的位置是零。Nginx 压根不认识 .php 这个后缀,请求打过来它要么返回 404,要么当成静态文件去磁盘上找一个不存在的文件。PHP 解释器没有机会被唤起,漏洞也就没有入口。
十分钟紧张,换来三行输出。值了。
不过在关掉这条短信之前,我倒是想把这事彻底搞明白——毕竟这个洞的评分是 9.2,而且我身边用 WordPress 建站的朋友一抓一大把。于是我把官方的技术公告翻了一遍。
这个洞,到底是怎么回事
两个 CVE 编号不同,但病根是同一个:PHP 在处理"出错"的时候,脑子不清醒。
第一个洞:内存耗尽时的"半成品"
PHP 有个配置叫 memory_limit,意思是"单个请求最多能吃掉多少内存,超了直接掐死"。这是道保险丝,防止一个烂请求把整台服务器拖死。
问题就出在"掐死"的那一瞬间。
打个比方:
你在拼一张桌子,四条腿只装了一条。 老板突然喊"时间到,收工!"——这就是
memory_limit触发。 正常流程应该把半成品零件收回去放好。 但清理代码犯了个错:它把这张歪桌子当成了成品,还照着图纸去拆。 而图纸指的位置,可能正好是上一批零件堆里、被攻击者塞过东西的地方。
技术一点的说法:memory_limit 超限时,PHP 用 longjmp() 直接跳出去中断请求。这一跳可能发生在内部请求数据只初始化了一半的时刻——典型场景是正在解析 multipart 表单上传。接着清理流程把未初始化的无效数据当成有效数据去释放内存,而那块内存里可能还留着攻击者能控制的内容。内存被破坏,然后被利用成执行任意代码。
这条最要命的地方在于:在 Apache + mod_php 的部署方式下,一个请求就能触发,而且是在你的业务代码执行之前。也就是说,哪怕你的 index.php 里只写了一行 echo "hello",也可能被打穿。你代码写得再规范都不管用,因为这个洞在解释器里。
第二个洞:上个客人的东西没打扫干净
PHP-FPM 是进程池模式。一个 worker 处理完请求不会销毁,而是接着处理下一个——省去反复启动的开销。这是性能上的好事,但对"清理干净"的要求极高。
再打个比方:
客人退房了,保洁以为收拾完了,其实他一只拖鞋还留在床底下(内存已释放,但指针还指着那儿)。 新客人进来一屁股坐下,脚正好踩上那只拖鞋——踩空了,摔了。
这在程序世界里叫"释放后使用"(Use-After-Free),内存类漏洞里最经典的一种。属于某个请求的内部状态,在内存被释放后指针还残留着;如果中途发生一次致命错误,清理路径可能被跳过。等同一个 worker 接下一个请求时,某些逻辑只要在正常初始化之前抢先跑,读到的就是"上一位客人的残留"。
利用通常是两个请求配合:第一个制造残留,第二个去踩。前提是落到同一个 worker 上——多打几次总能碰上。
官方确认的一条现成路径是 session.upload_progress,也就是那个让 PHP 在上传大文件时显示"已上传 37%“的功能。这玩意儿绝大多数人根本不用,但它默认开着。
一句话对比
| CVE-2026-109618 | CVE-2026-109619 | |
|---|---|---|
| 病根 | 内存耗尽时的清理逻辑 | 请求之间的状态没重置干净 |
| 比喻 | 半成品当成品拆,拆错位置 | 上个客人的残留被下个客人踩到 |
| 漏洞类型 | 内存破坏(OOM corruption) | 释放后使用(Use-After-Free) |
| 门槛 | 单请求即可(mod_php 下) | 通常要两个请求命中同一 worker |
| 已知入口 | multipart 表单解析 | session.upload_progress、致命错误回溯 |
影响范围:比想象中广
修复版本是这几个:
| 分支 | 安全版本 |
|---|---|
| PHP 8.5 | ≥ 8.5.12 |
| PHP 8.4 | ≥ 8.4.27 |
| PHP 8.3 | ≥ 8.3.36 |
| PHP 8.2 | ≥ 8.2.35 |
| PHP 8.1 及更早 | 已 EOL,无官方更新(官方给了 7.4 / 8.0 / 8.1 的源码回溯补丁) |
运行方式上,官方原话是”所有处理 Web 请求的 PHP 安装",点名了 PHP-FPM、Apache + mod_php(风险最高)、php-cgi、FrankenPHP、LiteSpeed。
但真正让我意外的是连带范围。很多产品是把 PHP 打包在里面的:WordPress、Drupal、Joomla 这些建站程序,Laravel 这类框架,宝塔面板的网站功能,各种 NAS 套件、路由器管理页,还有一堆基于 php:8.x-fpm 的容器镜像。
荷兰 NCSC 甚至专门建了个仓库登记"哪些软件内嵌了受影响的 PHP"。也就是说——就算你的主站不用 PHP,服务器上、内网里、某个设备的后台,也可能藏着一个 PHP。
顺便记一下时间线,感受一下这次的节奏:
| 时间 | 事件 |
|---|---|
| 10-20 | PHP 官方罕见地提前打招呼:明天发紧急补丁 |
| 10-21 13:30 UTC | 补丁发布 |
| 10-21 当天 | 欧盟 CERT-EU、英国 NCSC、新加坡 CSA、瑞士 BACS 同日发高危通告 |
| 10-22 | 技术细节和 PoC 已在网上流传 |
官方会"提前预告",说明严重到必须让运维提前排队;多个国家级机构在同一天发告警,这种规格一般只给"人人都用的东西出了要命的洞"。而 PoC 一旦公开,就意味着拿现成脚本的扫描会按小时来——这种事从不按天算。
为什么这次让人有点火
翻公告的时候还看到一篇 PHP 基金会自己写的复盘,读起来挺不是滋味。
大意是:2024 年 PHP 做了多年来第一次外部安全审计,审计收尾时就摸到了 OOM 这个问题的边。同年已经有人推演出"理论上能打成 RCE",但当时没找到具体路径,其他有明确影响的漏洞更紧急,就压下去了。
后来要修,发现有几种改法,每种都有代价:破坏向后兼容、引入新的拒绝服务风险、或者要改一堆第三方扩展(基本没法协调)。安全团队是共识制,没人敢拍板,怕选错。于是"修—停—再修"反复折腾了几个月。
基金会原话是:
我们不打算掩饰,这次延迟是一次结构性的失败,既有决策流程的问题,也有人力不足的问题。
这段话我看了两遍。它说明的事其实很朴素:开源基础设施的维护者常年人手不够。 你依赖的东西越是"默默无闻地支撑一切",出问题时越是没人兜底。PHP 撑着大半个互联网,但它的安全团队要靠基金会筹钱养。
如果你的站真是 PHP
我把这段整理出来了,万一以后要帮朋友处理,不用再翻公告。
唯一真正的解法是升级。 官方说得很直白:没有完整的缓解措施。
| |
宝塔用户:软件商店 → PHP → 更新或切到 8.3.36 / 8.4.27 / 8.5.12,然后必须重启 PHP(老进程还在跑旧代码,光换版本没用)。
Debian / Ubuntu:
| |
Docker 的话,docker restart 没用,镜像得重建:
| |
升完还得确认三件事,这几个坑我都见过:
| |
实在升不了级,只能做临时缓解,但要有心理准备:这些措施只降低特定路径的暴露面,不会让你的安装变安全。
针对 OOM 那个洞:
| |
注意别用 RLIMIT_AS / RLIMIT_DATA 去兜内存——它们造成的分配失败正好会走进漏洞路径。要用容器或 cgroup 限制,并且配成"只杀出问题的那个 worker"。
针对残留状态那个洞:
| |
改完必须重启所有 worker,而且要写在 php.ini、FPM pool 或服务器配置里——不能靠 ini_set() 或 .user.ini,那时候请求已经在解析了,晚了。
还有个容易忽略的点:用 error_reporting、display_errors 或者 @ 把警告藏起来是没用的。问题不在"显示",在于它被触发了。那些无效的、已废弃的 per-request 设置,得真的清掉。
最后,补丁只修漏洞,不修复已经发生的入侵。如果站已经被人摸进去过,升级之后还得查:
| |
真发现痕迹就别自己清了,断网、留证据、找专业应急。
虚惊一场之后
关掉那条短信的时候,我有点庆幸当初选了静态站。
但冷静下来想,这种"庆幸"其实是有代价的——静态站把 PHP 这一整类风险换掉了,代价是评论、点赞、搜索这些动态功能全得另外搭:Twikoo 要单独跑一个 Node 服务连 MongoDB,看板要单独跑一个 Go 服务,每一个都是新的攻击面。风险没有消失,只是挪了位置。
所以这条短信对我来说不算白紧张。至少现在我清楚地知道:自己的站靠什么在跑、哪些端口开着、万一真中招该从哪查起。这种"知道"比"应该没事吧"踏实得多。
至于我那些用 WordPress 的朋友,我已经把消息转过去了——他们的 PHP 是真得升级。
参考资料
- PHP 官方公告(2026-10-21):https://www.php.net/archive/2026.php#2026-10-21-5
- PHP 基金会复盘:https://thephp.foundation/blog/2026/10/21/todays-php-security-release-and-how-were-strengthening-php-security/
- 技术公告 GHSA-w8hw-w9fx-j79w:https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w
- 技术公告 GHSA-6wmf-4rv5-p3rp:https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp
- CERT-EU 安全通告 2026-018:https://cert.europa.eu/publications/security-advisories/2026-018
- 英国 NCSC:https://www.ncsc.gov.uk/news/critical-security-issue-affecting-php
- 阿里云漏洞通告:https://www.aliyun.com/notice/118746
- 内嵌 PHP 的受影响产品清单(NCSC-NL):https://github.com/NCSC-NL/php2shell
