一直有人说 PHP 是世界上最好的语言——这次 7 成网站用行动(被迫)投了赞成票,然后 PHP 官方连夜发布了两个 CVSS 9.2 的 RCE 补丁。
于是我这个 Oracle Linux 7.9 的"钉子户",也必须把去年 php 翻车的升级工程再战一次。
TL;DR:PHP 官方于 2026-10-21 发布紧急安全更新,修复两个 CVSS 9.2 的未授权 RCE 漏洞(代号 PHP2Shell),修复版本为 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12。由于 Oracle Linux 7.9 自带依赖库全面低于 PHP 8.5 门槛,本文记录在不动系统库的前提下,通过"独立目录源码编译 + PKG_CONFIG_PATH 引导"完成升级的全过程,以及 12 个踩坑点的规避方案。
一、漏洞背景:为什么必须马上升级
2026 年 10 月 21 日,PHP 官方罕见地提前一天预告安全发布节奏,随后放出两个 CVSS v4.0 评分 9.2 / 10 的严重漏洞补丁,业内统称为 PHP2Shell:
| CVE 编号 | 触发条件 | 危害 |
|---|---|---|
| CVE-2026-109618 | PHP 处理内存耗尽错误时的异常退出流程,请求体尚未完成初始化即进入清理阶段 | 内存破坏 → RCE |
| CVE-2026-109619 | FPM worker 处理新请求时未清理上一请求的内部状态,触发 Use-After-Free | 内存破坏 → RCE |
两者均可在默认配置下被未授权攻击者通过特制 HTTP POST 请求直接利用,影响范围覆盖 PHP 5.4 至 8.5.11——也就是说,与你写的业务代码质量无关,哪怕站点只输出一行 Hello World 也会中招。
官方修复版本:PHP 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12。PHP 8.1 及以下已 EOL,没有官方补丁,必须升级。
风险等级:Cloudflare CEO 公开披露,漏洞公告发布后 45 分钟内攻击尝试从数千次飙升至数十万次,2 小时内累计数百万次;亚马逊蜜罐早在 10 月 17 日就已捕获野外利用尝试。升级是唯一可靠的修复方式,不存在完整的临时缓解方案。
📌 我在 2025 年 10 月踩过一次 LNMP 升级 PHP 8.3.25 失败回滚的坑(点这里回看),这次借助 PHP2Shell 的契机,终于把升级一鼓作气推到了 8.5.12。
影响面有多广?——7 成网站都在枪口下
根据 W3Techs 于 2026 年 10 月 22 日(漏洞发布次日)的统计,在能识别服务端编程语言的网站中,约 69.3% 使用 PHP。也就是说,全球近 7 成网站——无论规模大小——都在本次 PHP2Shell 的潜在射程之内。
其中受影响最大的几类场景:
| 受影响对象 | 原因 |
|---|---|
| WordPress / Drupal / Joomla! 站点 | CMS 核心即 PHP,漏洞在引擎层,与主题插件无关,必须升级 PHP 本体 |
| cPanel / Plesk 虚拟主机面板 | 面板随附 PHP 运行时,用户无权自行编译升级,需等厂商推送 |
| 部分厂商设备、Windows 桌面应用 | 内嵌 PHP 运行时,往往是最容易被遗忘的死角 |
💡 给读者的自查建议:
- WordPress 用户:后台「站点健康 → 工具」可直接查看当前 PHP 版本;
- 虚拟主机用户:登录 cPanel/Plesk 的 “Select PHP Version / PHP Settings” 查看并切换版本;
- 自建服务器:
php -v一条命令见分晓。PHP 8.1 及以下没有官方补丁,7 成网站里没升级的每一个,都是攻击者的活靶子。
二、本次升级的核心挑战
系统太老,PHP 太新。 Oracle Linux 7.9 自带的 libxml2、OpenSSL、zlib、libcurl、libzip 等基础库版本全部低于 PHP 8.5 的最低要求,且不能直接覆盖系统库——否则会破坏 yum、sshd、curl 等核心工具,风险极高。
最终采用 “逐个源码编译依赖到独立目录 + 环境变量引导 PHP 链接” 的方式完成,全程零系统库覆盖,可随时回滚。
升级前自查清单
- [x] 备份
/usr/local/php与/usr/local/php/etc全套配置 - [x] 备份
php-fpm.conf、www.conf、php.ini - [x] 记录当前
php -v、php -m输出,用于升级后比对 - [x] 准备好回滚脚本(见文末)
- [x] 选择业务低峰期执行 (个人博客,没啥访问)
三、最终成功的步骤
1. 安装编译工具链
系统自带的 GCC 4.8.5 无法编译 PHP 8.5 的 SHA-NI 优化代码,必须安装 devtoolset-11:
|
1 2 3 4 |
sudo yum install -y centos-release-scl sudo yum install -y devtoolset-11-gcc devtoolset-11-gcc-c++ devtoolset-11-binutils scl enable devtoolset-11 bash gcc --version # 确认 11.2.1 |
⚠️ 关键:后续所有编译操作都必须在
scl enable devtoolset-11 bash 的会话中执行,否则 configure 阶段会莫名报错。
2. 逐个编译依赖库(全部装到独立目录)
| 依赖 | 系统版本 | PHP 8.5 要求 | 编译安装路径 |
|---|---|---|---|
| libxml2 | 2.9.1 | ≥ 2.9.4 | /usr/local/libxml2-new(2.9.14) |
| OpenSSL | 1.0.2k | ≥ 1.1.1 | /usr/local/openssl3(3.5.0) |
| zlib | 1.2.7 | ≥ 1.2.11 | /usr/local/zlib-new(1.3.2) |
| libcurl | 7.29.0 | ≥ 7.61.0 | /usr/local/curl-new(8.23.0) |
| libiconv | 系统 iconv 不满足 | errno 检查失败 | /usr/local/libiconv(1.17) |
| libzip | 无 / 过旧 | ≥ 0.11 | /usr/local/libzip-new(1.11.4,CMake 构建) |
每个库编译安装后,都执行:
|
1 2 |
echo "/usr/local/xxx/lib" | sudo tee /etc/ld.so.conf.d/xxx.conf sudo ldconfig |
|
1 2 3 4 5 6 7 |
cd /usr/local/src wget https://www.php.net/distributions/php-8.5.12.tar.gz sha256sum php-8.5.12.tar.gz # 官方 .tar.bz2 SHA256: ffb60edc437ced63b5137e4984db379a8111c50049c711510d8b958277efc1a8 # 请以 https://www.php.net/releases/8_5_12.php 公布的 .gz 哈希为准 tar -xzf php-8.5.12.tar.gz cd php-8.5.12 |
重新激活 devtoolset-11(新开 shell 忘了就前功尽弃):
|
1 2 |
scl enable devtoolset-11 bash gcc --version # 确认 11.2.1+ |
设置环境变量:
|
1 2 3 |
export PKG_CONFIG_PATH=/usr/local/libzip-new/lib64/pkgconfig:/usr/local/openssl3/lib64/pkgconfig:/usr/local/libxml2-new/lib/pkgconfig:/usr/local/zlib-new/lib/pkgconfig:/usr/local/curl-new/lib/pkgconfig export LDFLAGS="-Wl,-rpath,/usr/local/libzip-new/lib64 -Wl,-rpath,/usr/local/openssl3/lib64 -Wl,-rpath,/usr/local/libxml2-new/lib -Wl,-rpath,/usr/local/zlib-new/lib -Wl,-rpath,/usr/local/curl-new/lib -Wl,-rpath,/usr/local/libiconv/lib" |
configure 命令(最终可用版本):
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
./configure \ --prefix=/usr/local/php85 \ --with-config-file-path=/usr/local/php85/etc \ --with-config-file-scan-dir=/usr/local/php85/etc/php.d \ --enable-fpm \ --with-fpm-user=www \ --with-fpm-group=www \ --enable-cli \ --with-openssl \ --with-curl=/usr/local/curl-new \ --with-zlib \ --with-zlib-dir=/usr/local/zlib-new \ --with-iconv=/usr/local/libiconv \ --with-libzip=/usr/local/libzip-new \ --with-mysqli=mysqlnd \ --with-pdo-mysql=mysqlnd \ --enable-mbstring \ --enable-xml \ --with-zip \ --enable-bcmath \ --enable-soap \ --enable-sockets \ --without-pdo-sqlite \ --without-sqlite3 |
|
1 2 |
make -j$(nproc) sudo make install |
安装后三项验证:
|
1 2 3 |
/usr/local/php85/bin/php -v /usr/local/php85/bin/php -m | grep -E <span class="hljs-string">'iconv|curl|openssl|mbstring|xml|zip'</span> ldd /usr/local/php85/bin/php | grep -E <span class="hljs-string">'ssl|curl|xml|z|zip'</span> |
|
1 2 3 4 5 6 7 8 9 10 11 12 |
# 1. 停止旧 FPM lnmp php-fpm stop # 2. 备份旧 PHP 目录 mv /usr/local/php /usr/local/php-$(date +%Y%m%d)-bak # 3. 创建软链接(LNMP 脚本通过此路径访问) ln -s /usr/local/php85 /usr/local/php # 4. 复制并编辑配置 cp /usr/local/php85/etc/php-fpm.conf.default /usr/local/php85/etc/php-fpm.conf cp /usr/local/php85/etc/php-fpm.d/www.conf.default /usr/local/php85/etc/php-fpm.d/www.conf |
编辑 www.conf,确保 Socket 路径与 Nginx 一致:
|
1 2 3 4 |
listen = /tmp/php-cgi.sock listen.owner = www listen.group = www listen.mode = 0660 |
⚠️ 如果
www.conf中存在listen.acl_users,它会覆盖owner/group设置,必须注释掉。
|
1 2 3 |
# 5. 启动 lnmp php-fpm start lnmp nginx reload |
四、12 个坑速查表(Ctrl+F 定位即用)
| # | 现象 | 根因 | 规避方案 |
|---|---|---|---|
| 1 | dnf 命令不存在 |
OL7.9 只有 yum | 直接用 yum 即可 |
| 2 | --enable-opcache 警告 |
PHP 8.5 OPcache 已随核心编译 | 删除该选项即可 |
| 3 | configure 报 libxml2 过旧 | 系统 2.9.1 < 2.9.4 | 源码编译 2.9.14 到 /usr/local/libxml2-new,走 PKG_CONFIG_PATH |
| 4 | OpenSSL 1.0.2k 过旧 / curl 8.23 拒绝 1.1.1 | curl 8.23 明确要求 OpenSSL 3.0+ | PHP 与 curl 统一用 OpenSSL 3.5.0,避免两套并存 |
| 5 | configure 选错 OpenSSL | shell 残留旧 PKG_CONFIG_PATH |
unset PKG_CONFIG_PATH 后用 env PKG_CONFIG_PATH=... ./configure 临时指定 |
| 6 | zlib 1.2.7 过旧 | < 1.2.11 | 编译 zlib 1.3.2 到独立目录 |
| 7 | iconv errno 检查失败 | glibc iconv 不满足 | 编译 GNU libiconv 1.17,--with-iconv=/usr/local/libiconv |
| 8 | libzip .pc 找不到 |
CMake 默认装到 lib64 |
ldconfig 与 PKG_CONFIG_PATH 路径都用 lib64 |
| 9 | attribute(target("sha")) is unknown |
GCC 4.8.5 不识别 SHA-NI | 安装 devtoolset-11。不存在 --disable-hash-sha-ni 这个选项,别去试 |
| 10 | SQLITE_DBCONFIG_ENABLE_LOAD_EXTENSION 未定义 |
系统 SQLite 太老 | 业务无依赖则 --without-pdo-sqlite --without-sqlite3 |
| 11 | 切换后 502,Socket 属主是 root | FPM 主进程 root 启动,Socket 默认 root | 显式配置 listen.owner/group/mode,注释 listen.acl_users,用 ls -l /tmp/php-cgi.sock 验证 |
| 12 | php-fpm 不在 bin |
PHP 8.5 装到 sbin |
/usr/local/php85/sbin/php-fpm,但 LNMP 通过软链接访问,软链接指对即可 |
五、上线后验证清单
升级完成、业务恢复正常后,不要马上关会话,跑完这 6 项再收工:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
# 1. 版本必须 ≥ 8.5.12 /usr/local/php85/bin/php -v # 2. 关键扩展加载正常 /usr/local/php85/bin/php -m | grep -E 'iconv|curl|openssl|mbstring|xml|zip' # 3. 二进制动态链接指向新编译库 ldd /usr/local/php85/bin/php | grep -E 'ssl|curl|xml|z|zip' # 4. Socket 属主为 www ls -l /tmp/php-cgi.sock # 5. FPM 进程监听正常 ps -ef | grep php-fpm # 6. 访问 phpinfo() 页面(截图存档) |
|
1 2 3 4 5 |
lnmp php-fpm stop rm /usr/local/php ln -s /usr/local/php-$(date +%Y%m%d)-bak /usr/local/php lnmp php-fpm start lnmp nginx reload |
关键:备份目录名带时间戳,回滚只需改一次软链接指向,不动任何库文件,5 分钟内可恢复业务。
七、复盘与建议
- PHP 8.1 及以下没有补丁,仍在用的读者请尽快评估升级到 8.2.35 / 8.3.36 / 8.4.27 / 8.5.12。
- 临时缓解不可靠:WAF 规则、禁用某些函数等手段都挡不住该漏洞的利用链,升级是唯一正解。
- AI 时代的漏洞节奏变了:本次漏洞攻防两侧都在提速——常态化保持 PHP 版本跟进,比临时救火成本低得多。
- 老系统 ≠ 不能升级:本篇证明 OL7.9 + 源码编译依赖同样能跑到 PHP 8.5.12,关键是"独立目录 + PKG_CONFIG_PATH + rpath"三板斧,不动系统库零风险。
参考链接
- PHP 官方 8.5.12 发布公告:https://www.php.net/releases/8_5_12.php
- PHP 官方 8.x ChangeLog:https://www.php.net/ChangeLog-8.php
- W3Techs 服务端语言占有率统计(2026-10-22):https://w3techs.com/technologies/details/pl-php
- WatchTowr 漏洞 FAQ:https://watchtowr.com/intelligence/php-cve-2026-109618-faq
- Datadog Security Labs 分析:https://securitylabs.datadoghq.com/articles/php2shell-critical-vulnerabilities-in-php
- 我上次升级失败的复盘:https://blog.wanjie.info/2025/10/lnmp-php-8-3-25-upgrade-failure-rollback/

文章评论