PHP2Shell:打补丁,还是让 AI 升版本
2026 年 10 月 21 日,PHP 官方发布了一次紧急安全更新。这次更新修复的两个漏洞被评为 CVSS 9.2(满分 10), 攻击者不需要任何账号,只要向网站发一个构造好的请求,就可能在服务器上执行任意代码。安全圈给它起了个代号: PHP2Shell。
这不是某个框架、某个插件的漏洞,而是 PHP 语言引擎本身的问题。也就是说,只要你的服务器上跑着没打补丁的 PHP,你想装多少安全插件都没用。
一、这次到底是什么漏洞
PHP2Shell 是两个漏洞的组合,编号分别是 CVE-2026-109618 和 CVE-2026-109619。它们各自独立成立, 也常被组合成一次攻击。
| 编号 | 官方描述 | 成因 | 后果 |
|---|---|---|---|
| CVE-2026-109618 | RCE via OOM corruption(内存耗尽导致的内存破坏) | PHP 处理「内存耗尽」时用的是一种跳转机制。如果内存恰好在请求内存还没初始化完的那一刻分配失败, PHP 会留下半成品状态,后面又把它当成有效数据使用 | 远程代码执行 |
| CVE-2026-109619 | RCE via stale global state(全局状态残留) | PHP 进程会保留一部分上一个请求的全局状态。如果某个操作在重置它之前就被中断(比如致命错误), 下一个请求可能读到已经释放的内存 | 远程代码执行 |
官方在公告里说得很直白:这两个问题都在「内存管理」和「错误处理」里,而这两块是几乎每个请求都会经过的地方。 这正是它们既严重、又难修的原因 —— 动它容易出兼容性问题,也容易影响性能。
二、影响范围:谁中招
先说结论:所有使用 PHP 5.4 及以后版本、且没有打这个补丁的网站,都在影响范围内。
按运行方式看风险高低
不同的部署方式,被利用的难易程度差别很大。这一点很关键,因为它决定你该多着急。
| 运行方式 | 风险 | 说明 |
|---|---|---|
| Apache + mod_php | 最高 | 即使请求初始化失败,它也照样执行收尾流程, 所以攻击可以在任何业务代码运行之前发生,无需登录、无需任何前置条件,用默认配置就能打通 |
| PHP-FPM | 中高 | 初始化失败时它会直接丢弃这个子进程,挡住了前面那条路; 但源码分析认为正常启动后再触发仍然可能,目前还没有完整的 FPM 攻击演示 |
| FrankenPHP | 中高 | 行为和 mod_php 类似,推测同样可能存在与业务无关的攻击路径 |
| LiteSpeed (LSAPI) | 中 | 模型接近 FPM,判断需要配合应用层漏洞才容易打 |
| php-cgi | 需评估 | 视部署形态而定 |
换句话说:如果你的站是 Apache + mod_php,请当天处理,不要等。
按版本看修复情况
| PHP 分支 | 受影响的版本 | 修复后的版本 | 该分支支撑到 |
|---|---|---|---|
| 8.5 | 低于 8.5.12 | 8.5.12 | 2029-12-31 |
| 8.4 | 低于 8.4.27 | 8.4.27 | 2028-12-31 |
| 8.3 | 低于 8.3.36 | 8.3.36 | 2027-12-31 |
| 8.2 | 低于 8.2.35 | 8.2.35 | 2026-12-31(就剩两个月) |
| 8.1 及更早 | 官方不再发布修复版本,只提供了源码补丁 | 已停止支持 | |
注意 8.2 这一行:它今年年底就到寿了。这次能修,但下一次不一定还有。这一点在选升级目标时要想进去。
你的框架干净也没用
因为漏洞在引擎层,所以 WordPress、Drupal、Joomla、MediaWiki、Magento、Laravel、Symfony 这些 本身代码写得再规范,跑的 PHP 没打补丁一样中招。这一点和过去那些框架漏洞完全不同,别按老经验处理。
三、现在被利用了吗
三条公开信息摆在一起,结论很明确:
| 时间 | 事件 |
|---|---|
| 10 月 21 日 07:43 UTC | Cloudflare 观测到针对性的早期攻击 —— 比官方发布修复版本还早 |
| 10 月 21 日 13:30 UTC | PHP 发布 8.5.12、8.4.27、8.3.36、8.2.35 |
| 10 月 21 日 | 美国 CISA、欧盟 ENISA、CERT-EU 等多国机构同时发布预警 |
| 10 月 22 日 | 我国国家网络安全通报中心发布预警 |
| 10 月 26 日 | CISA 把两个编号列入「已遭利用」目录(KEV) |
但同时也要说清楚另一面:安全研究机构 Sansec 认为不太可能出现爆发式扫描攻击。 理由是这套攻击需要了解目标服务器的具体情况,不像当年的 Log4Shell 那样一个请求通吃所有机器; 公开的验证代码在默认配置的 PHP 上跑不通,多数还要配合目标应用本身存在文件包含漏洞。
这个判断不影响你要做的事,只影响你有多慌。正确的态度是:不必停服,但必须当天修。 因为已经有人在利用了,而且工具已经公开。
下面把三条路分别讲清楚。先看它们的整体关系:

四、第一条路:直接升级(唯一完整的修复)
官方公告里有一句原话:升级到包含修复的版本,是唯一完整的缓解措施。 其他所有办法都是打折扣的,包括官方自己给的配置建议。
操作上就三步:
- 确认当前版本:命令行执行
php -v,网站环境里看phpinfo()或后台的运行环境页 - 装到对应分支的最新修复版,然后重启 PHP-FPM 或 Apache
- 再查一次
php -v,确认真的换过来了
第三条最容易被跳过。用包管理器升级了,但服务没重启,跑在内存里的还是旧进程 —— 这种「升了等于没升」的情况非常常见。
共享主机用户怎么办
如果你用的是虚拟主机、面板托管,自己管不了 PHP 版本,那就把工单提给服务商, 问清楚一句话:「你们部署的 PHP 是否已包含 CVE-2026-109618 和 CVE-2026-109619 的修复?」 对方答不上来的,就当成没修。
五、第二条路:不能升级时打补丁(只能是临时的)
现实里确实有一批系统升不了:PHP 8.1 及更早的老项目、依赖某个老扩展的遗留系统、被面板或厂商锁死版本的商业软件。 这些怎么办?
官方给了什么
PHP 安全团队为 7.4、8.0、8.1 这三个已经停止支持的分支单独发布了源码补丁。 同时,各大发行版的包维护者把它移植进了旧版本:打完之后版本号还是原来的(比如还是 8.1.34), 只是包的发版号变了。
| 你的情况 | 该找谁 | 备注 |
|---|---|---|
| Linux 发行版自建 | 发行版的软件源(Debian/Ubuntu 可关注 deb.sury.org 与 ppa:ondrej/php) | 升级包但版本号不变,装完重启服务 |
| 云托管 / PaaS | 平台方 | 部分平台已自动给旧版本打了补丁,问一句确认 |
| 厂商交付的商业系统 | 厂商 | 别自己改,改动会把维保关系搞复杂 |
| 完全自己编译运行 | 只能自己编译 | 见下一节 |
自己改源码要做什么
最差的情况是:你的 PHP 是自己编译的,版本又老,没人给你提供现成包。这时候只能对照官方的修复提交, 把补丁移植到你的版本上。
但必须提醒一句 —— 这个补丁不是改一行就能完事的。 官方修复 CVE-2026-109618 的做法,是把「内存耗尽」原有的跳转机制整个换掉: 改成在下一个虚拟机安全点触发一次延迟的「软内存耗尽」,另外再设一个更高的阈值,一旦到达就直接硬退出进程。 这动的是内存管理的核心流程,牵涉的文件不止一处。
所以真要做移植,正确的顺序是:
- 找到官方针对 7.4 分支的那份补丁(它对旧版本的适配价值最高,很多维护者也是拿它往更低版本移的)
- 先在你的版本上试打,把冲突逐个解掉,别一次改完
- 改完必须编译 + 跑你自己的业务回归,而不是只看能不能启动
- 记清楚这次改了哪些文件、对应哪个提交 —— 下次官方再修漏洞,你得重来一遍
为什么它只能是临时的
| 问题 | 后果 |
|---|---|
| 分支本身已经停止支持 | 这次修好了,下一个漏洞没人管你 |
| 你改的是引擎核心代码 | 后续小版本更新会冲突,补丁会不断漂移 |
| 没有官方的回归测试覆盖 | 改坏了可能体现为某些请求偶发崩溃,很难排查 |
| 维护成本挂在人身上 | 当初改的人走了,这套东西就成了黑盒 |
结论很简单:打补丁是「今天先活下来」,升级才是「以后不再担这个心」。
六、第三条路:用 AI 把项目升到受支持版本
前面说过,升级到受支持版本是唯一完整的修复。但很多人的真实顾虑不是「不想升」,而是 「升级要改一大堆代码,我改不动」。
这正是 AI 现在能真正帮上忙的地方 —— 不是替你做决定,而是把升级过程中那些又琐碎又费眼力的活接过去: 把所有调用过时写法的地方找出来、逐处改掉、跑一遍看哪里还报错、再改、再跑。
AI 能做什么,不能做什么
| 能 | 不能 |
|---|---|
| 扫描全站代码,列出所有与目标版本不兼容的写法 | 替你判断某项业务逻辑在新版本下应该怎么表现 |
| 批量改写语法与废弃 API,并解释每处为什么改 | 凭空补齐你缺失的回归测试用例 |
| 根据报错反推出根因,给出修改方案 | 猜出你没告诉它的第三方依赖真实行为 |
| 对比官方迁移文档,逐条核对你是否漏改 | 替你做「什么时候切换、切多少流量」的决策 |
一句话:AI 负责比对和改代码,业务语义和用例覆盖仍然要人来把关。
七、AI 升级 PHP 版本的处理流程
把项目丢给 AI 说一句「帮我升到 PHP 8.3」,大概率得到一堆似是而非的改动。要它真能干活, 关键是按阶段推进,每阶段有明确产出。下面这六步是实打实按顺序走的。

第一步:先盘点,别急着改
动手之前必须把现状摸清并写成一份清单,这份清单后面要反复回看:
- 当前 PHP 版本、运行方式(mod_php / FPM / 其他)
- 框架及其版本、包管理器(Composer)的依赖树
- 用到的所有 PHP 扩展,逐个确认目标版本上还剩下
- 当前版本离停止支持还有多久
- 有没有扩展在目标版本上已被移除或改名
这一阶段最容易翻车的是扩展。语法问题 AI 改得很快,但某个扩展在新版本上没有了, 那就是要换方案的事,不是改代码能解决的。先查清楚,免得改到一半才发现。
第二步:先建安全网,再动代码
这一步经常被跳过,然后就会在第三步后悔。具体做三件事:
| 动作 | 为什么 |
|---|---|
| 代码与数据库完整备份,并真的验证能还原 | 备份过但没试过还原,等于没有备份 |
| 把回滚步骤写成可执行的脚本或文档 | 出事时人是要慌的,凭记忆回滚必漏步 |
| 整理一套能跑起来的回归用例 | 没有它,你无法判断自己改坏了什么 |
用例不一定要完整。核心链路、登录、支付、后台的增删改查,能覆盖到这些就足以拦住大部分事故。
第三步:扫描兼容性,拿到改动清单
让 AI 通读代码,产出一份清单,每行包含:文件、行号、问题代码、为什么不兼容、改成什么。 这一份清单是后续所有工作的总账。
扫描要覆盖这四类,缺一类都会在后面返工:
- 语法层:新版本收紧的写法、废弃的语法、类型声明的强制化
- 函数层:被废弃、被移除、行为有变化的函数
- 扩展层:某个扩展消失后,依赖它的函数怎么替代
- 依赖层:Composer 里的包本身支不支持目标 PHP 版本
最后一条常被忽略:你的框架或依赖包可能根本不支持目标版本。这时候先升框架,再升 PHP, 顺序反了会卡死。
第四步:逐级升,不要一步到位
从 7.4 直接跳到 8.4,报错会一次涌上来几百条,你根本分不清哪条是根因。
正确做法是一次只升一个大版本:7.4 → 8.0 → 8.1 → 8.2 → 8.3, 每一级升完、跑通、提交,再升下一级。每一级只处理这一级的破坏性变更,报错数量可控,出问题也容易定位到是哪次升级引入的。
多花的这些时间,会在你排查问题时十倍地赚回来。
第五步:双版本并行验证
光靠「能启动」不算通过。要同时跑新旧两个版本的环境,把同一套用例各跑一遍,逐项比对结果。
重点看这几类差异:
- 页面输出是否完全一致(哪怕多了个空格也算差异)
- 接口返回的字段名、类型、顺序有没有变
- 数字计算结果有没有微妙漂移(浮点、日期、时区最常见)
- 日志里的告警和废弃提示有没有新增
这一步 AI 特别合适:把两边的输出丢给它,让它列出差异并判断哪些是预期内的、哪些是真问题。 比人一行行肉眼比对可靠得多。
第六步:灰度切换,盯着指标再全量
不要一次切完。先放一小部分流量,观察错误率、响应时间、PHP 错误日志和慢查询。
稳定一段时间再逐步放量。切换过程中最容易出的事不是直接报错,而是性能下降或 偶发超时 —— 这类问题不会立刻爆,会在全量之后才集中暴露。
让 AI 干活时,话该怎么说
直接给一句「帮我升级 PHP 版本」效果很差。下面这段可以直接改着用:
项目路径是 /path/to/project,当前 PHP 版本 8.1,目标版本 8.3。
请按这四步来做,每一步做完先给我看结果,我确认后再进行下一步:
1. 扫描全项目,列出所有在 8.2 和 8.3 中不兼容的写法,输出成表格:文件、行号、原代码、问题、建议改法
2. 检查 composer.json 里的依赖,逐个确认是否支持 PHP 8.3,列出不支持的
3. 我确认清单后,按文件分批改,每批改完告诉我改了哪些、为什么改
4. 最后给我一份回归测试应该重点覆盖的清单
不要一次改完所有文件,也不要改动我没有确认的部分。
三个要点:先出清单不动手、分批而非一次到底、明确禁止它擅自扩范围。 这三条规定能挡掉绝大多数「AI 改出一堆看不懂的代码」的情况。
八、怎么选:一张表说清
| 你的情况 | 该走哪条路 | 紧急程度 |
|---|---|---|
| PHP 8.2 ~ 8.5,装到修复版就能用 | 直接升级 | 当天 |
| PHP 8.2 但马上要停支持了 | 先升级止血,同时规划升到 8.3 或 8.4 | 当天 + 本季度 |
| PHP 8.1 及更早,能编译能改 | 先打官方源码补丁止血,同时启动升级 | 当天 + 一个月内 |
| PHP 8.1 及更早,托管在别人那 | 找服务商要补丁版包,同时催升级方案 | 当天问清 |
| 老系统、动不了、也没人维护 | 打补丁 + 前置网关拦截 + 限制入站请求,然后把它列进替换计划 | 当天 + 排期 |
九、几个容易踩的坑
| 坑 | 说明 |
|---|---|
| 升了但没重启服务 | 包升级了,跑在内存里的还是旧进程。务必重启 FPM 或 Apache 再验证 |
| 以为改配置能代替升级 | 官方给了 memory_limit 等配置建议,但明确写了这不是完整缓解。
而且若内存分配在底层失败,那条隐患路径仍然存在 |
| 用 RLIMIT_AS 或 RLIMIT_DATA 限内存 | 官方明确说不要用。这两个限制会让内存申请失败,反而正好触发漏洞路径 |
| 以为关掉错误显示就安全了 | CVE-2026-109619 那条与错误报告设置无关。改 error_reporting、加 @、
关掉 display_errors 都不起作用 |
| 以为有 WAF 就没事 | WAF 只能提供部分防护,不能替代升级 |
| 只改应用不改环境 | 漏洞在 PHP 引擎,改业务代码一行都没用 |
十、最后
这次的事情值得记住的,不只是「赶紧升级」这四个字。
它暴露的是一个结构性问题:那些长期停在停止支持版本上的系统,平时看不出任何问题, 一旦出事就没有干净的出路。打补丁是能救命,但你是在给一台已经没人维护的机器手工续命, 每来一次漏洞就要重复一遍,而且每一次都比上一次更难。
所以真正的建议是:今天该打补丁打补丁、该升级升级;同时认真想一下,你的项目打算什么时候离开那个版本。 这件事拖得越久,代价越大 —— 而现在的 AI 工具,确实已经把「改不动」这个理由削弱了很多。
本文信息依据 PHP 官方安全公告(GHSA-w8hw-w9fx-j79w、GHSA-6wmf-4rv5-p3rp)、 PHP 基金会 2026 年 10 月 21 日发布的说明、PHP 官网支持版本页,以及 CISA 于 2026 年 10 月 21 日发布 并于 10 月 26 日更新的预警。具体的修复版本与处置建议,请以官方公告为准。