2026 年 10 月 21 日,PHP 官方发布紧急安全公告:PHP 8.5.12、8.4.27、8.3.36、8.2.35 同步修复两个可导致未授权远程代码执行的严重漏洞,CVSS v4.0 评分均为 9.2。本文先拆解公告要点,再按 PHP 7.4 / 8.0 / 8.1 / 8.2 / 8.3 / 8.4 / 8.5 七个版本分别给出可执行的升级路线与兼容性注意事项。
一、公告核心:两个 9.2 分 RCE 漏洞
这次不是常规的 bug 修复版本,而是安全版本。两个漏洞都能在应用代码执行之前被触发,其中 mod_php 场景的风险最高。
| CVE 编号 | 问题类型 | 触发机制 |
|---|---|---|
| CVE-2026-109618 | OOM 内存耗尽处理缺陷 | 内存耗尽中断了 PHP,内部请求数据只完成部分初始化,请求清理阶段可能把非法数据当成合法数据处理 |
| CVE-2026-109619 | 全局状态残留 | 请求级内部状态在内存释放后仍然存活,同一 worker 处理的下一个请求可能读到脏状态 |
影响范围:所有对外提供 Web 请求的 PHP 安装,包括 PHP-FPM、Apache mod_php、php-cgi、FrankenPHP 与 LiteSpeed。官方已确认 mod_php 存在无需认证即可在应用代码运行前触发的利用方式。
二、先确认自己在哪个版本
升级前必须先确认业务实际加载的 PHP 二进制,避免出现"CLI 升了、FPM 没升"的情况。
php -v
php -m
php --ini
# 查看 FPM 实际加载的二进制与配置
ps -ef | grep php-fpm
php-fpm -v
# 站点侧验证(以 FPM 为准,而不是 CLI)
curl -s https://your-site/phpinfo.php | grep -i "PHP Version"
PHP_VERSION 与 php_sapi_name(),可以快速确认线上到底是 FPM 还是 mod_php,以及具体补丁号。三、版本支持周期总表
PHP 每个分支的节奏是:活跃支持 2 年,之后仅修关键安全问题 2 年,满 4 年进入 EOL。对照下表可以先判断"能不能只打补丁"。
| 分支 | 初始发布 | 活跃支持至 | 安全支持至 | 当前状态 | 本次官方版本 |
|---|---|---|---|---|---|
| PHP 7.4 | 2019-11-28 | 2021-11-28 | 2022-11-28 | EOL | 无(仅源码 backport 分支) |
| PHP 8.0 | 2020-11-26 | 2022-11-26 | 2023-11-26 | EOL | 无(仅源码 backport 分支) |
| PHP 8.1 | 2021-11-25 | 2023-11-25 | 2025-12-31 | EOL | 无(仅源码 backport 分支) |
| PHP 8.2 | 2022-12-08 | 2024-12-31 | 2026-12-31 | 仅安全维护 | 8.2.35 |
| PHP 8.3 | 2023-11-23 | 2025-12-31 | 2027-12-31 | 仅安全维护 | 8.3.36 |
| PHP 8.4 | 2024-11-21 | 2026-12-31 | 2028-12-31 | 活跃支持 | 8.4.27 |
| PHP 8.5 | 2025-11-20 | 2027-12-31 | 2029-12-31 | 当前稳定版 | 8.5.12 |
可以看到,7.4、8.0、8.1 已经完全没有官方安全更新,这次公告只为它们提供了最小源码 backport,适合自行编译或使用 LTS 厂商补丁的场景。普通业务应当尽快跨到受支持的分支。
四、分版本升级攻略
1. PHP 7.4:不要修修补补,直接规划跨版本
PHP 7.4 已于 2022 年 11 月 28 日 EOL。官方这次只提供了 PHP-7.4-CVE-2026-109618-109619 源码分支,没有官方二进制包。
- 能自编译:拉取该分支,按原有 configure 参数重新编译并替换。
- 用发行版或商业 LTS:联系厂商确认是否已回移补丁。
- 两者都不满足:把升级列为最高优先级,目标直接定 PHP 8.3 / 8.4 / 8.5,不要停在同样 EOL 的 8.0。
7.4 → 8.0 的主要断点:
each()、create_function()、money_format()等函数被移除。- 默认错误级别改为
E_ALL,未定义变量从 Notice 升级为 Warning,很多老项目会突然"报错变多"。 - 字符串与数字比较规则变化,例如
0 == "foo"在 PHP 8 中为false。 - 非静态方法被静态调用会直接致命错误。
real类型、魔术引号、PHP 4 风格构造器全部移除。assert()不再执行字符串参数。- 算术与位运算传入非法类型会抛出
TypeError。
建议先用兼容性扫描把问题一次性捞出来,再动生产环境。
composer require --dev phpcompatibility/php-compatibility
vendor/bin/phpcs -p . --standard=PHPCompatibility --runtime-set testVersion 8.5
# 也可以用 rector 做自动化改写
composer require --dev rector/rector
vendor/bin/rector process src --dry-run
2. PHP 8.0:同样 EOL,用 backport 或直接跳到 8.3+
PHP 8.0 于 2023 年 11 月 26 日 EOL,官方提供 PHP-8.0-CVE-2026-109618-109619 源码分支。生产环境不建议继续停留。
8.0 → 8.1 的升级要点:
- 把
null传给非空的内部函数参数开始产生弃用警告,例如strlen(null)。 - 字符串插值
${var}写法弃用,改用{$var}。 strftime()、gmstrftime()、FILTER_SANITIZE_STRING、Serializable接口弃用。- 新增 enum、readonly 属性、Fiber、
never返回类型、交集类型,可顺手做代码现代化。
3. PHP 8.1:已 EOL,本次只能走源码 backport
PHP 8.1 的安全支持已于 2025 年 12 月 31 日结束。官方公告写得很明确:8.1 及更早版本不再接收更新,只提供最小源码回移。
- 短期:使用
PHP-8.1-CVE-2026-109618-109619分支自行编译,或等发行版/LTS 厂商补丁。 - 中期:至少升到 8.2.35,生产建议直接到 8.4.27 或 8.5.12。
8.1 → 8.2 的升级要点:
- 动态属性弃用:给未声明属性赋值会触发 Deprecated,需要显式声明属性,或加
#[\AllowDynamicProperties]。 utf8_encode()、utf8_decode()弃用,改用mb_convert_encoding()。${}字符串插值正式弃用。strtolower()、strtoupper()不再受 locale 影响。- 对
$GLOBALS的整体写入受到限制。 - 新增 readonly class、DNF 类型、独立
null/false/true类型、Random扩展。
4. PHP 8.2:必须升到 8.2.35,并在年底前迁出
8.2 目前仅剩安全维护,2026 年 12 月 31 日 EOL,只剩两个多月。这次务必先升到 8.2.35 堵住漏洞,同时把迁移到 8.4 / 8.5 排进计划。
# Debian / Ubuntu(Ondrej 源已提供 8.2.35)
sudo apt update
sudo apt install --only-upgrade php8.2-cli php8.2-fpm php8.2-common \
php8.2-mysql php8.2-curl php8.2-mbstring php8.2-xml php8.2-zip
# RHEL 系(Remi)
sudo dnf upgrade -y php82-php-cli php82-php-fpm php82-php-common
sudo systemctl restart php8.2-fpm
php-fpm -v
8.2 → 8.3 的升级要点:
- 无参调用
get_class()、get_parent_class()弃用,需显式传对象。 MT_RAND_PHP随机模式弃用。- 新增类型化类常量、
#[\Override]属性、json_validate()、动态类常量获取Foo::{$name}。 - readonly 属性在
clone过程中允许重新初始化。
5. PHP 8.3:升到 8.3.36,重点是 8.4 的隐式 nullable
8.3 的活跃支持已于 2025 年 12 月 31 日结束,安全支持到 2027 年 12 月 31 日。本次升级到 8.3.36 即可。
8.3 → 8.4 最容易踩的坑:隐式 nullable 参数弃用。下面这种写法在 8.4 会持续产生弃用警告:
<?php
// PHP 8.4 之前可以,8.4 起弃用
function findUser(int $id = null) {}
// 正确写法:显式声明可空
function findUser(?int $id = null) {}
其他 8.4 变化:
E_STRICT常量弃用。- 新增 property hooks、非对称可见性、
new链式调用可省略括号。 - 新增
array_find()、array_any()、array_all()、mb_trim()等函数。 - 新增
#[\Deprecated]用户态弃用标记。
6. PHP 8.4:升到 8.4.27,活跃支持年底结束
8.4 仍在活跃支持期,但活跃支持到 2026 年 12 月 31 日结束。先升级到 8.4.27,再评估是否在年内切到 8.5。
8.4 → 8.5 的主要变化:
| 特性 | 说明 |
|---|---|
管道操作符 |> | 把值从左到右依次传给可调用对象,减少中间变量 |
clone() with | 克隆对象的同时覆盖指定属性,适合 readonly 类 |
#[\NoDiscard] | 返回值未被使用时产生警告,防止忽略重要返回 |
| URI 扩展 | 内置 RFC 3986 / WHATWG URL 解析、规范化与修改能力 |
| 常量表达式中的闭包 | 静态闭包与一等可调用可作为属性参数等常量表达式 |
| 持久化 cURL Share 句柄 | 跨请求复用连接初始化结果,降低重复开销 |
7. PHP 8.5:当前稳定版,升到 8.5.12
8.5 是当前推荐的生产分支,活跃支持到 2027 年 12 月 31 日,安全支持到 2029 年 12 月 31 日。本次直接升级到 8.5.12。
五、通用升级操作(按部署方式)
Ubuntu / Debian(Ondřej Surý 源)
sudo apt update
sudo apt install -y software-properties-common
sudo add-apt-repository -y ppa:ondrej/php
sudo apt update
# 以 PHP 8.5 为例,安装业务实际用到的扩展
sudo apt install -y php8.5-cli php8.5-fpm php8.5-common \
php8.5-mysql php8.5-curl php8.5-mbstring php8.5-xml php8.5-zip
# 确认仓库里拿到的补丁号
apt-cache policy php8.5-fpm
# CLI 默认版本切换
sudo update-alternatives --set php /usr/bin/php8.5
# FPM 重启
sudo systemctl restart php8.5-fpm
# Apache 用户还需要切换模块
sudo a2dismod php8.2
sudo a2enmod php8.5
sudo systemctl restart apache2
CentOS / RHEL / Rocky / AlmaLinux(Remi 源)
sudo dnf install -y https://rpms.remirepo.net/enterprise/remi-release-9.rpm
sudo dnf module reset php -y
sudo dnf module enable php:remi-8.5 -y
# 升级现有 PHP 包
sudo dnf upgrade -y 'php*'
# 检查版本并重启 FPM
php -v
sudo systemctl restart php-fpm
Windows
- 从 windows.php.net 下载 8.5.12 对应线程安全 / 非线程安全压缩包。
- 先备份现有 PHP 目录和
php.ini。 - 替换二进制文件,保留原
php.ini,按需更新extension_dir与扩展行。 - 重启 Apache / IIS / php-fpm,执行
php -v与php -m核对。
Docker
FROM php:8.5.12-fpm
RUN docker-php-ext-install pdo_mysql mysqli mbstring
# 构建并滚动更新
# docker compose build
# docker compose up -d
源码编译
./configure --prefix=/usr/local/php-8.5.12 \
--with-fpm --enable-mbstring --with-pdo-mysql --with-curl
make -j"$(nproc)"
sudo make install
# 用新前缀安装,验证通过后再切换软链
sudo ln -sfn /usr/local/php-8.5.12 /usr/local/php
sudo systemctl restart php-fpm
六、升级前检查清单
- 备份
php.ini、扩展清单(php -m)、FPM pool 配置与业务代码。 - 对比升级前后的扩展:
diff <(php7.4 -m) <(php8.5 -m)。 - 扫描废弃函数与语法:
each(、create_function、${、隐式 nullable 参数。 - 执行
composer check-platform-reqs,确认依赖声明的 PHP 版本范围。 - 先升级 CLI 并跑测试,再升级 FPM;先灰度一台,再全量。
- 保留旧版本包或旧镜像 tag,确保可在 10 分钟内回滚。
七、实在无法立即升级:官方临时缓解
官方强调这些措施只能降低特定攻击路径的暴露面,不能替代升级。
针对 CVE-2026-109618(OOM 路径)
- 将
memory_limit设为-1,并通过 FPM pool 或 Apache 配置的php_admin_value下发,防止应用层改回有限值。 - 限制请求体:不需要上传时
post_max_size = 8M,需要上传时post_max_size = 32M,并保证upload_max_filesize小于它。 - 在 Web 服务器或代理层限制解码后的请求大小、字段数、multipart 段数、请求头数量与嵌套深度,且要覆盖没有有效
Content-Length的请求。 - 使用容器或 cgroup 做可用性隔离,避免使用
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。 - 清理每次请求都会触发诊断信息的废弃配置项,隐藏报错(
error_reporting、display_errors、@)没有作用。 - 修复攻击者可触发的致命错误与执行超时,包括回调、上传、导入与清理逻辑中的失败路径。
八、升级后验证
php -v
php-fpm -v
php -r 'echo PHP_VERSION, " / ", php_sapi_name(), PHP_EOL;'
# 确认 FPM 已使用新二进制
ps -ef | grep php-fpm
# 健康检查
curl -I https://your-site/
# 关键 ini 是否生效
php -i | grep -E "memory_limit|post_max_size|session.upload_progress"
九、行动建议
| 当前版本 | 立即动作 | 中期规划 |
|---|---|---|
| PHP 7.4 | 自编译 backport 分支或找 LTS 厂商 | 直接迁移到 8.3 / 8.4 / 8.5 |
| PHP 8.0 | 自编译 backport 分支或找 LTS 厂商 | 直接迁移到 8.3 / 8.4 / 8.5 |
| PHP 8.1 | 自编译 backport 分支或找 LTS 厂商 | 至少升 8.2.35,建议 8.4 / 8.5 |
| PHP 8.2 | 立即升 8.2.35 | 2026-12-31 前迁出 |
| PHP 8.3 | 立即升 8.3.36 | 评估 8.5.12 |
| PHP 8.4 | 立即升 8.4.27 | 活跃支持年底结束,规划 8.5 |
| PHP 8.5 | 立即升 8.5.12 | 保持补丁跟进 |
安全补丁没有"下次再说"。
能升就升,升不了就隔离,别让一个未认证请求跑在旧版本上。