PHP-FPM 的子进程是复用的,一个 worker 忙完这个请求,接着处理下一个,中间不退出。php2shell 里的 CVE-2026-109619,php.net 公告的描述正好落在这条缝上:属于上一个请求的内部状态,在内存释放之后还留着,同一个 worker 接着干活时,可能在正常初始化之前就用上了它。所以「我们是 FPM,跟 mod_php 不一样」这个想法,放在这枚洞上不太站得住。老版本升不了怎么过渡,站里 1820 那帖聊过。这篇只说已经在 8.x 受支持分支上跑 FPM 的机器。
官方对 FPM 到底怎么写的
- php.net 的安全公告列的受影响范围是所有处理 Web 请求的 PHP,FPM 明确在里面,只是官方认为 mod_php 风险最高。
- 修复版本是 8.5.12、8.4.27、8.3.36、8.2.35。php-src 上两份 GitHub 安全公告标的受影响范围一致:各分支凡是低于上面这几个号的都算。
- 109619 的公告(GHSA-6wmf-4rv5-p3rp)里写着:session 那条路径的远程代码执行,在 mod_php 和 FPM 上都验证过。
- 109618 的公告(GHSA-w8hw-w9fx-j79w)倒是承认,FPM 上还没有演示出完整利用,但从源码分析看它同样受影响。说「FPM 难打」的,大概只说中了这一半。
- CISA 10 月 26 日把两枚 CVE 都收进了 KEV 目录,也就是确认已有在野利用。KEV 条目没写被利用的是哪种 SAPI,别替它补结论。
KEV 条目给美国联邦机构定的处理期限是 10 月 29 日,从收录算起只有三天。国内的机器当然不受这个约束,但这个期限说明 CISA 对这两枚洞的定性,跟「不急」完全不沾边。另外 php.net 提到,发行版和 LTS 厂商是提前拿到补丁的,用 apt、yum 装 PHP 的,可以直接去查仓库有没有更新包。8.2 的用户还要记一下:8.2.35 的发布说明写了这条分支 2026 年 12 月 31 日停止安全支持。
日志干净说明不了问题
自己拿靶机试了毫无反应,跟线上从未被碰过是两回事。109619 那份公告的原话是:没有报错记录不代表安全,失败也可能发生在 PHP 写出日志之前。Google Cloud 的处置文给的检测项里,专门有一条盯 FastCGI / PHP-FPM worker 反复重启(SIGSEGV,退出码 139)和 core dump。那篇也提醒,有的利用链既不崩溃,也不往 error.log 写东西。CISA 的建议是盯 PHP worker 去拉起 shell 或者下载工具的行为,这个比翻 error.log 靠谱。退出码 139 这种信号,平时常被当成扩展不稳定顺手略过,这次最好回头把 10 月下旬的监控翻一翻。
补丁装上之前
- 公网上的 FPM 机器排在最前面,CISA 的建议也是优先处理面向互联网的系统。
- 确认版本。发行版的包看 changelog 里有没有这两个 CVE 编号,Google 那篇给的写法是
apt-get changelog php-fpm或rpm -q --changelog php-fpm再 grep。 - 升级完软件包要重启 FPM 服务。磁盘上的文件换了,跑着的 worker 还是旧二进制。
- 在
php.ini、FPM pool 配置,或者在请求解析之前就生效的服务器配置里设session.upload_progress.enabled=0,然后重启所有 worker。php.net 特意说明别依赖ini_set()或 FPM 的.user.ini。 - 8.5 上再加一条
fatal_error_backtraces=0。 - 109619 的公告提到一种情况:nginx 等前端按请求给 FPM 传
PHP_VALUE/PHP_ADMIN_VALUE,这些值又在初始化前就报出诊断信息,这时 FPM 会有条件地暴露。用了这类写法的,把过时或写错的配置项清掉。 - 性能扛得住的话,公告还建议考虑
pm.max_requests=1,让每个 worker 只处理一个请求。 - 内存和上传大小相关的临时措施,php.net 公告里有完整列表,
memory_limit那条和容器或 cgroup 限制要一起看,别只抄半句。
这些都只是缩小暴露面。php.net 在四个版本的发布说明里都重复了这层意思:缓解措施不完整,只有升级才安全。
手上跑 FPM 的,你们这次是直接跟发行版的包升了,还是先改了配置排期再升?