运维知识
悠悠
2026年10月21日

全网紧急排查!PHP 连发两条 9.2 分高危漏洞:光 `php -v` 合格,你的线上可能还在裸奔!

10 月 21 日,PHP 官方发布了四个紧急安全版本:PHP 8.5.12、8.4.27、8.3.36 和 8.2.35。

它们修复了两条可能造成未认证远程代码执行的核心漏洞:CVE-2026-109618 和 CVE-2026-109619,CVSS v4.0 评分均为 9.2。

根据 PHP 官方安全公告,所有处理 Web 请求的 PHP 安装都需要排查,包括 PHP-FPM、Apache mod_php、php-cgi、FrankenPHP 和 LiteSpeed。官方认为 mod_php 风险尤其高,因为已经证明未认证请求能够在应用代码执行前触发攻击。

这意味着,无论你运行的是 WordPress、Laravel、Magento,还是自研 PHP 系统,问题都不只是某个框架、插件或业务接口。升级 Composer 依赖、关闭一个路由、修改业务代码,都不能代替修补 PHP 本身。

早期消息强调“补丁发布后的黄金 15 分钟”,但真正落地时,我更关心三件事:你是否找全了线上所有 PHP,是否确认 Web 流量实际使用的是新二进制,以及旧 Worker 是否已经全部退出。

这次事件最值得记住的一句话是:PHP 软件包升级完成,不等于漏洞修复完成;只有处理请求的旧进程全部退出,修复才真正生效。

两条漏洞到底发生了什么

两条漏洞都和内存生命周期有关,但触发机制并不相同。完整技术细节应以 PHP 官方发布的两份 GitHub Security Advisory 为准。

CVE-2026-109618:OOM 处理中的内存破坏

CVE-2026-109618 官方技术公告显示,PHP 在处理请求期间发生内存限制错误时,内部请求数据可能只完成了部分初始化。程序随后进入请求清理流程,却把这些无效数据当成正常对象处理,最终造成内存破坏并可能演变成远程代码执行。

日志中的下面一类错误值得重点关注:

PHP Fatal error: Allowed memory size of ... bytes exhausted

但它不能单独证明服务器遭到攻击。大数组、图片处理、批量导入、低效 SQL 或递归调用,同样可能导致 PHP 内存耗尽。

正确的判断方法是建立关联:

  • 同一时间是否出现来源异常的高频请求;
  • 请求体、表单字段或 multipart 分段是否明显异常;
  • PHP-FPM Worker 是否崩溃或被重新拉起;
  • 系统是否出现段错误、Core Dump 或 OOM Kill;
  • Web 目录、临时目录和计划任务是否出现未知文件;
  • PHP 进程是否建立了不符合业务特征的外连。

单条内存错误只能作为线索,不能直接写进事故报告当作入侵证据。

CVE-2026-109619:释放后的全局状态残留

CVE-2026-109619 官方技术公告显示,某次请求使用的内部状态可能在底层内存释放后继续残留。同一个长期运行的 Worker 处理后续请求时,可能在正常初始化之前重新使用这段过期状态。

官方确认的相关路径包括会话上传进度;PHP 8.5 还涉及致命错误回溯路径。

这也解释了为什么修改配置后必须重启全部 PHP Worker:旧进程不仅可能继续加载旧代码,还可能保留旧配置及跨请求残留的内部状态。

先别急着重启,10 分钟内把真实资产找全

应急处置最常见的错误,是登录一台服务器执行 php -v,看到版本合格就宣布修复完成。

CLI、PHP-FPM 和 Apache mod_php 可能来自不同软件包,甚至可能属于不同版本。容器里的 PHP 与宿主机也没有必然关系。第一步必须确认谁在实际处理线上请求。

下面都是只读检查,可以先执行并保存结果。

检查命令行版本、SAPI 和配置来源:

date -Is
hostname -f

php -r 'echo "version=", PHP_VERSION, PHP_EOL,
             "sapi=", PHP_SAPI, PHP_EOL,
             "ini=", php_ini_loaded_file(), PHP_EOL;'

php --ini

如果输出中的 sapi 是 cli,只能说明命令行环境的版本,不能证明 Web 请求也由这个版本处理。

继续检查实际运行的服务:

ps -eo pid,lstart,user,cmd \
  | grep -E '[p]hp-fpm|[p]hp-cgi|[a]pache2|[h]ttpd|[f]rankenphp|[l]itespeed'

重点记录 PID、启动时间、运行用户和二进制路径。如果一台主机存在多个 FPM 主进程,需要逐个确认监听端口、Unix Socket、Pool 配置和对应站点。

PHP-FPM 环境还可以检查:

command -v php-fpm || true
php-fpm -v 2>/dev/null || true
php-fpm -tt 2>&1 | sed -n '1,160p'

部分发行版会把二进制命名为 php-fpm8.4、php8.4-fpm,或者将不同版本放入独立目录。不能因为 php-fpm 命令不存在,就认定服务器没有运行 FPM。

Debian、Ubuntu 系统查看已安装的软件包:

dpkg-query -W \
  -f='${Package}\t${Version}\t${Status}\n' \
  'php*' 2>/dev/null \
  | grep 'install ok installed'

RHEL、Rocky Linux、AlmaLinux、CentOS Stream 可执行:

rpm -qa --qf '%{NAME}\t%{VERSION}-%{RELEASE}\t%{ARCH}\n' \
  | grep -E '^php'

不要只按上游版本字符串判断发行版软件包是否安全。Linux 发行版或商业 LTS 厂商可能把安全补丁反向移植到旧版本,同时保留原来的主版本号。最终要结合发行版安全公告、软件包 Release 字段和变更记录判断。

PHP 官方的版本生命周期可以在 Supported Versions 页面查询;上游版本变更记录可以查看 PHP 8 ChangeLog。

容器和 Kubernetes 别只检查宿主机

容器环境先输出运行清单:

docker ps --format \
  'table {{.ID}}\t{{.Image}}\t{{.Names}}\t{{.Status}}'

docker exec <container> php -r \
  'echo PHP_VERSION, " ", PHP_SAPI, PHP_EOL;'

还要确认镜像摘要,避免同一个浮动标签对应不同构建:

docker inspect <container> \
  --format '{{.Config.Image}} {{.Image}}'

Kubernetes 环境先确认上下文,避免查错集群:

kubectl config current-context

kubectl get pods -A \
  -o custom-columns='NS:.metadata.namespace,POD:.metadata.name,IMAGES:.spec.containers[*].image'

再对确认过的 PHP 容器执行:

kubectl exec -n <namespace> <pod> -c <container> -- php -v

不要对整个大型集群无差别并发执行 kubectl exec。更稳妥的做法是先导出 Deployment、StatefulSet、DaemonSet、Job 和 CronJob 的镜像清单,按镜像摘要去重,然后检查代表性容器。

资产可按暴露程度分级:

  • P0:公网直接可达,且处理上传、导入、富文本、支付回调等复杂请求;
  • P1:公网可达,但前面有 CDN、WAF 或 API 网关;
  • P2:仅内网访问,但会接收不可信来源的数据;
  • P3:离线任务、开发测试和已停止实例。

优先级只决定处置顺序,不代表低级别资产可以不修。

暂时不能升级时,官方建议怎样止损

PHP 官方明确表示:不存在完整的配置缓解方案,升级是唯一安全选择。

下面的配置只能降低部分已确认攻击路径的风险,不能将主机重新标记为安全。

缓解 CVE-2026-109618

官方建议将 memory_limit 设置为 -1,并放在应用不能自行覆盖的位置。PHP-FPM 可以在对应 Pool 中配置:

php_admin_value[memory_limit] = -1

这可以去掉攻击者相对容易控制的 PHP 内存限制失败路径,但底层真实内存分配失败仍可能进入危险流程。

这个配置存在明显的可用性风险:失控请求可能继续占用内存,直到容器或主机 OOM。因此必须同时使用容器或 cgroup 控制资源边界,确保单个异常 Worker 不会拖垮整台服务器。

不要用 RLIMIT_AS 或 RLIMIT_DATA 代替容器内存边界。PHP 官方特别提醒,这两类限制本身可能制造漏洞所需要的内存分配失败条件。

同时应收紧请求体。对不需要文件上传的业务,可以从官方建议的参考值开始:

post_max_size = 8M
upload_max_filesize = 2M

必须上传文件的业务可以评估:

post_max_size = 32M
upload_max_filesize = 24M

这些数值不是所有业务的通用答案。变更前应统计真实上传大小,否则可能误伤镜像上传、数据导入和支付回调。

Nginx 可在对应 server 或 location 中同步限制:

client_max_body_size 8m;

变更前先保存当前配置并进行语法检查:

nginx -T > /var/tmp/nginx-before-php2shell.txt
nginx -t

限制还应覆盖没有可信 Content-Length 的请求,以及 multipart 分段数、表单字段数、Header 数量和输入嵌套深度。

缓解 CVE-2026-109619

在请求解析前关闭会话上传进度:

php_admin_value[session.upload_progress.enabled] = 0

PHP 8.5 还应临时关闭致命错误回溯:

php_admin_value[fatal_error_backtraces] = 0

这些配置应写入 php.ini、FPM Pool 或 Apache 服务端配置。不要依赖应用里的 ini_set(),也不要依赖 .user.ini,因为应用代码执行到这些配置前,漏洞路径可能已经触发。

配置完成后先校验:

php-fpm -tt
nginx -t

然后从负载均衡摘除单个实例,等待活动连接排空,再重启全部 PHP Worker。

WAF、IP 白名单、关闭上传入口、限制管理后台都可以作为额外防线,但目前不存在能够证明覆盖全部变体的通用 WAF 规则。

disable_functions 也不能修复 PHP 内核的内存破坏。即使禁用了 exec、system、shell_exec,攻击者仍可能通过其他 PHP 能力读写文件、连接网络或窃取进程凭据。

补丁怎样上线,才不会留下旧 Worker

PHP 上游确认的最低修复版本如下:

PHP 分支最低上游修复版本
PHP 8.58.5.12
PHP 8.48.4.27
PHP 8.38.3.36
PHP 8.28.2.35

官方源码和 Windows 二进制可以从 PHP Downloads 获取。Linux 生产环境应优先使用发行版或可信 LTS 厂商提供的软件包,不建议直接用源码文件覆盖系统包。

Debian、Ubuntu 环境先检查候选版本:

apt-cache policy php8.4-fpm php8.4-cli php8.4-common

确认仓库已经提供安全更新后,再进入变更窗口:

sudo apt-get update
sudo apt-get install \
  php8.4-fpm php8.4-cli php8.4-common

RHEL 系发行版先查看可用更新:

dnf check-update 'php*'
dnf info php-fpm

确认供应商安全公告和目标版本后执行:

sudo dnf upgrade 'php*'

实际环境可能使用模块流、SCL、Remi、源码编译或内部 RPM 仓库。不要为了追求上游版本号,临时混入一个未经验证的软件源。

PHP-FPM 环境完成升级后,对已摘流实例进行完整重启:

sudo systemctl restart php8.4-fpm
sudo systemctl status php8.4-fpm --no-pager

Apache mod_php 环境需要重启 Apache:

sudo apachectl configtest
sudo systemctl restart apache2

RHEL 系服务名通常是 httpd:

sudo apachectl configtest
sudo systemctl restart httpd

仅执行 reload 可能让旧 Worker 在排空请求期间继续存活。这次事件要求确认所有旧 Worker 已经退出,不能只看到 reload 成功就结束变更。

推荐的灰度顺序是:

  1. 保存软件包、配置、进程和监控快照;
  2. 从负载均衡摘除一台实例;
  3. 等待活动请求排空;
  4. 升级 PHP 及相关扩展;
  5. 完整重启实际使用的服务单元;
  6. 验证版本、进程、扩展和核心业务;
  7. 恢复 1%~5% 流量;
  8. 观察稳定后逐批扩大。

出现以下情况应停止扩容:

  • 核心接口出现新的 5xx;
  • PHP-FPM 或 Apache 启动失败;
  • 错误率明显高于未升级实例;
  • P95、P99 延迟持续恶化;
  • PHP 扩展缺失或加载失败;
  • 上传、Session、序列化或数据库驱动不兼容;
  • 持续出现崩溃、段错误或 Worker 异常退出。

回滚不能简单等于把存在漏洞的旧版本重新开放公网。更安全的方案是保持异常节点摘流,修复配置或扩展兼容问题;如果业务必须暂时运行旧镜像,应隔离网络、限制入口并应用临时缓解措施。

怎样证明线上真的修好了

先检查 CLI 和软件包版本:

php -v

dpkg-query -W \
  -f='${Package}\t${Version}\n' \
  'php8.4*' 2>/dev/null

然后检查处理请求的进程启动时间:

ps -eo pid,lstart,user,cmd \
  | grep -E '[p]hp-fpm|[a]pache2|[h]ttpd'

所有 Worker 的启动时间都应该晚于升级和重启时间。

在 Linux 上还可以检查进程是否引用已删除的旧二进制或动态库:

for pid in $(pgrep -f 'php-fpm|apache2|httpd'); do
  printf '%s\t' "$pid"
  readlink "/proc/$pid/exe"
done

sudo lsof +L1 \
  | grep -E 'php|php-fpm|apache2|httpd'

如果进程路径后出现 (deleted),或者 lsof +L1 显示 PHP 进程仍引用已经替换的动态库,通常说明旧进程没有完全退出。应继续保持节点摘流,确认真实的 systemd 服务单元并完成重启。

不要为了检查 Web SAPI 版本,长期在公网放置 phpinfo() 页面。它可能泄露环境变量、扩展、目录结构和服务器配置。更安全的办法是在受控网络通过现有健康检查返回构建版本,或者创建仅绑定回环地址、验证后立即删除的临时诊断入口。

恢复流量期间至少观察:

  • HTTP 4xx、5xx 和应用异常率;
  • PHP-FPM 活跃、空闲 Worker 数;
  • max_children 是否频繁耗尽;
  • Worker 重启、崩溃和段错误数量;
  • P95、P99 延迟;
  • 登录、上传、支付回调和任务队列;
  • 主机或容器内存;
  • OOM Kill 和容器重启次数。

只有版本、进程、业务和监控四类证据全部通过,才能认定变更完成。

一个脱敏后的典型处置场景

下面用一个脱敏后的典型场景说明,时间、主机名和数据均为示例。

某团队维护 28 个 PHP 实例,CMDB 记录全部运行 PHP 8.4。应急盘点时,CLI 检查确实显示 8.4,但进程清单暴露出三个例外:两台遗留站点仍通过 Apache mod_php 运行 PHP 7.4,一个定时扩容节点使用了六个月前的旧容器镜像。

团队没有全量重启,而是先把公网 FPM 集群分成四批。第一批摘流两台实例,升级至供应商提供的修复包,重启 PHP-FPM,再使用登录、文件上传和支付回调三组请求验证。

CLI 版本检查通过后,工程师在 lsof +L1 输出中发现一个 FPM 主进程仍引用带 (deleted) 标记的旧动态库。

继续排查发现,这台机器的业务 Pool 由一个模板化 systemd 单元管理,而自动化脚本只重启了默认 php-fpm 服务。软件包虽然已经升级,真正处理流量的 Worker 却仍然运行旧代码。

如果团队只看 php -v,这台服务器就会被错误标记为已修复。最终他们保持节点摘流,重启真实的模板单元,确认旧 Worker 全部退出,再逐步恢复流量。

两台 PHP 7.4 站点没有可确认的供应商软件包,团队先把管理入口限制到 VPN,关闭不必要的上传功能,增加请求体限制,并将实例从公网负载均衡移除。完成兼容测试后,再迁移到受支持的 PHP 8.4 镜像。

这条证据链很清楚:CMDB给出初始范围,进程列表发现遗漏,lsof 证明旧库仍被加载,systemd 单元解释了自动化失效,灰度流量和业务监控验证了最终修复。

EOL 版本怎么办

PHP 8.1 及更早分支已经退出 PHP 项目的常规安全支持。部分 Linux 发行版和商业 LTS 厂商可能提供反向移植补丁,应优先使用经过签名、具有正式安全公告的软件包。

考虑到此次漏洞的严重性,PHP 安全团队还提供了三个最小源码回移分支:

这些分支不代表旧版 PHP 恢复了官方生命周期,也不是可以直接安装的通用发行版软件包,主要面向本来就具备源码构建和维护能力的团队。

如果生产环境没有成熟的自编译、测试、签名和发布链路,不建议在重大漏洞窗口期临时切换到手工编译。更稳妥的处理顺序是:

  • 查询操作系统或商业 LTS 厂商是否提供反向移植补丁;
  • 核对软件包签名、构建来源和安全公告;
  • 没有可信补丁时,摘除公网流量或隔离服务;
  • 应用临时缓解措施争取迁移时间;
  • 完成兼容测试后升级到受支持分支。

今晚可以直接照着执行的检查清单

  • 务必同时检查 CLI、FPM、mod_php、php-cgi 和容器版本,因为 php -v 不能代表线上 Web SAPI。
  • 不要仅按上游版本字符串判断发行版软件包,反向移植补丁需要结合供应商公告和 Release 字段确认。
  • 不要把更新 Composer、WordPress 或 Laravel 当成此次修复,漏洞位于 PHP 核心请求处理路径。
  • 不要把 WAF 或 disable_functions 当成完整缓解,它们无法消除内存破坏和状态残留。
  • 设置 memory_limit=-1 时,必须评估真实 OOM 和拒绝服务风险,并用容器或 cgroup 控制故障边界。
  • 禁止用 RLIMIT_AS 或 RLIMIT_DATA 代替官方建议的资源隔离,它们可能制造危险的内存分配失败。
  • 关闭 session.upload_progress.enabled 后务必重启每个 Worker,否则旧进程仍保留原配置和内部状态。
  • 不要只 reload 后立即宣布完成,应核对进程启动时间以及是否仍引用 (deleted) 的旧文件。
  • 不要在未摘流时批量重启全部节点,否则一次配置或扩展兼容问题就可能演变成全站中断。
  • 回滚不能等于把有漏洞的旧版本重新开放公网;无法升级的节点应隔离、限流并尽快迁移。
  • 不要把单条内存耗尽或致命错误日志直接认定为攻击,必须结合请求、进程、外连和文件变化建立证据链。
  • 务必保留升级前后的软件包、配置、进程、日志和监控快照,否则无法证明修复是否完整。
  • 不要长期暴露 phpinfo() 页面,其中可能包含路径、扩展、环境变量和服务器配置。
  • EOL 分支如果没有可信厂商补丁,必须制定迁移计划;临时隔离只能争取时间。

漏洞参考文档

下面按可信度和用途整理相关资料。版本范围、漏洞原理、缓解措施及修复版本,应以 PHP 官方公告和官方 GitHub Security Advisory 为准。

官方一手资料

  1. PHP 官方安全公告:Critical remote code execution vulnerabilities, update now

    汇总受影响运行方式、漏洞原理、上游修复版本、临时缓解措施和旧分支处置说明。

  2. CVE-2026-109618 / GHSA-w8hw-w9fx-j79w

    CVE-2026-109618 的官方技术公告,重点解释 OOM 处理、请求数据部分初始化和清理阶段内存破坏。

  3. CVE-2026-109619 / GHSA-6wmf-4rv5-p3rp

    CVE-2026-109619 的官方技术公告,重点解释请求状态释放后的残留、跨请求复用及相关缓解配置。

  4. PHP 官方下载页面

    用于获取官方源码和 Windows 二进制。Linux 生产环境应优先核对发行版或 LTS 厂商的软件包。

  5. PHP 受支持版本

    用于确认不同 PHP 分支的主动支持、安全支持和停止维护时间。

  6. PHP 8 ChangeLog

    用于核对上游版本变更。发行版使用反向移植补丁时,还需查看发行版自己的安全公告。

CVE 数据库

  1. NVD:CVE-2026-109618
  2. NVD:CVE-2026-109619
  3. CVE.org:CVE-2026-109618
  4. CVE.org:CVE-2026-109619

漏洞刚发布时,NVD 的 CPE、评分和受影响版本字段可能仍在完善。页面信息与 PHP 官方公告暂时不一致时,应优先采用 PHP 官方结论,并持续关注数据库更新。

事件背景材料

  1. note:php2shell 漏洞与生存策略
  2. PANews/TradingView:PHP 披露两个严重级远程代码执行漏洞

这两篇文章适合了解漏洞披露前后的传播时间线,但不能代替官方技术公告。补丁发布前流传的“影响 PHP 5 以来全部版本”“默认配置均可直接利用”“漏洞位于某个特定解析模块”等内容,当时包含第三方转述或技术推测,引用时必须标明时间状态。

本文文档核对时间为 2026 年 10 月 21 日。漏洞公告、供应商补丁和检测规则仍可能更新,执行生产变更前应重新检查 PHP 官方公告,以及所用操作系统、容器镜像和 LTS 厂商的最新安全通知。

这次 PHP2Shell 事件真正考验的,不是团队能否在新闻出现后迅速执行一条升级命令,而是资产发现、变更灰度、进程验证和应急回滚是否形成了完整链路。

现在最值得立即执行的动作,是导出所有处理 Web 请求的 PHP 实例和镜像清单,将它们与四个上游修复版本或供应商安全公告逐一比对,并指定责任人确认每个旧 Worker 已经退出。

如果这份排查手册对你有帮助,欢迎点赞、在看并转发给仍在维护 PHP 服务的同事。关键漏洞面前,可靠的资产清单和可验证的升级流程,比任何“祖传经验”都更有价值。

公众号:耕云躬行录

个人博客:躬行笔记

文章目录

博主介绍

热爱技术的云计算运维工程师,Python全栈工程师,分享开发经验与生活感悟。
欢迎关注我的微信公众号@运维躬行录,领取海量学习资料

微信二维码