一、PHP安全漏洞解析
1.1、PHP安全漏洞总结
PHP 核心引擎存在两个可被未认证远程攻击者 利用的致命漏洞(CVE-2026-109618、CVE-2026-109619,合称 PHP2Shell ),CVSS v4.0 评分 9.2 ,被安全公司 WatchTowr 评为 10.0 满分。漏洞藏在 PHP 内部超过十年,最终由 AI 辅助的研究者打通利用链,影响全球约 70% 的网站(包括 WordPress、Drupal、Joomla、Laravel、MediaWiki/Wikipedia 等)。2026 年 10 月 21 日 PHP 一次性发布 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 四个安全版本修复。
1.2、PHP的两个核心漏洞
PHP在2026年10月21日发布漏洞警告与修复补丁新闻
https://www.php.net/archive/2026.php#2026-10-21-5
| 项目 | CVE-2026-109618 | CVE-2026-109619 |
|---|---|---|
| 漏洞类型 | OOM(内存溢出) 导致的 RCE(GHSA-w8hw-w9fx-j79w) |
失效全局状态 导致的 RCE(GHSA-6wmf-4rv5-p3rp) |
| 根因 | 当内部请求数据仅部分初始化时,内存溢出处理机制可能会中断 PHP 程序的运行。此时,请求处理机制可能会将无效数据误认为是有效数据来处理。 | 当某个请求所涉及的内部状态所对应的内存被释放后,该状态仍然会保留下来。随后,由同一个工作进程处理的其它请求可能会使用这个过时的状态,而不会被正常的初始化过程所清除。 |
| 攻击路径 | mod_php 下无需认证的请求、在应用代码运行前即可触发;PHP-FPM 可通过应用层路径触发 | 已在实际应用上演示利用 |
| 附带能力 | 可远程披露 PHP worker 进程内存(含密码、令牌、API 密钥、证书) | 与 Apache 类型混淆结合可跨 worker 破坏共享状态 |
1.3、影响范围
- 版本 :过去十年几乎所有的 PHP 版本均受其影响,最早可追溯到 PHP 5.4。
- 运行方式 :PHP-FPM、Apache (mod_php)、php-cgi、FrankenPHP、LiteSpeed 全部受影响,其中 mod_php 风险最高(已有演示攻击表明,未经认证的请求可以在任何应用代码运行之前触发攻击)。
- 应用:漏洞在 PHP 核心层,与上层应用代码是否安全无关 ------ 即使一个 "Hello World" 应用也可能中招。CNVD 已确认影响 WordPress、Drupal、Joomla、Laravel 等主流 CMS 和框架。
二、PHP安全漏洞的修复
2.1、PHP漏洞修复版本与处理方向
| PHP分支 | 本次修复至版本 | 处理方向 |
|---|---|---|
| PHP 8.2 | 8.2.35 | 更新至此版本,或含有相同修复补丁的后续版本 |
| PHP 8.3 | 8.3.36 | 更新至此版本,或含有相同修复补丁的后续版本 |
| PHP 8.4 | 8.4.27 | 更新至此版本,或含有相同修复补丁的后续版本 |
| PHP 8.5 | 8.5.12 | 更新至此版本,或含有相同修复补丁的后续版本 |
| PHP8.1及其更早版本 | 一些发行版和长期支持版本的服务商会提供相应的补丁来修复这些问题。请咨询您的服务商以获取详细信息,或者升级到受支持的版本。 |
鉴于此次漏洞的严重性,PHP 安全团队为 PHP 7.4、PHP 8.0 和 PHP 8.1 提供了必要的源代码补丁,以帮助那些需要从源代码进行开发的人士,或那些无法获得 LTS 版本支持的用户。
2.2、确认当前的PHP环境
bash
#确认当前PHP环境
#1-方法一:
#1.1-查看当前操作系统的版本
cat /etc/os-release
#1.2-查看php的所在路径
command -v php
#1.3-确认php的版本
php -v
#1.4-查看php的配置文件及其所在路径
php --ini
#1.5-查看php运行加载的模块
php -m
#1.6-查看php的运行进程及其情况
ps -eo pid,ppid,user,args | grep -E '[p]hp-fpm|[p]hp-cgi|[h]ttpd|[a]pache2'
#1.7-查看php的运行服务
systemctl list-units --type=service --all | grep -Ei 'php|httpd|apache2|nginx'
#2-方法二:在命令行环境查询到的可能与网站真实使用的PHP不同,因此可以在网站根目录下创建phpinfo.php文件,然后再浏览器访问【http://服务器IP/phpinfo.php】页面会显示 PHP 版本、运行模式、加载的配置文件、扩展及参数配置。
sudo tee phpinfo.php<<'EOF'
<?php
phpinfo();
?>
EOF
#注意:查看PHP的信息查看完成后,立刻删除该文件(因为会泄露大量服务器敏感信息,容易被黑客利用)
sudo rm -f phpinfo.php






2.3、备份与兼容性检查
bash
#将PHP的环境信息保存到文件中
PHP_UPGRADE_BACKUP="/var/backups/php-upgrade-$(date +%Y%m%d-%H%M%S)"
mkdir -p "$PHP_UPGRADE_BACKUP"
php -v > "$PHP_UPGRADE_BACKUP/php-version.txt"
php --ini > "$PHP_UPGRADE_BACKUP/php-ini.txt"
php -m > "$PHP_UPGRADE_BACKUP/php-modules.txt"
rpm -qa 'php*' > "$PHP_UPGRADE_BACKUP/php-rpm-packages.txt"
#把 PHP + PHP-FPM 的全部配置文件打包压缩,保留原有权限,存到 `$PHP_UPGRADE_BACKUP` 目录下
tar -czpf "$PHP_UPGRADE_BACKUP/php-config.tar.gz" \
/etc/php.ini /etc/php.d /etc/php-fpm.conf /etc/php-fpm.d
2.4、开始升级PHP
Remi 仓库,专注于提供多版本 PHP 的软件源。此次升级就用到它,Remi仓库网站是:https://rpms.remirepo.net/ 主要是给红帽系系统(如: RHEL、Rocky Linux、AlmaLinux、Fedora )提供 PHP 及相关扩展软件包。它不是 PHP 官方仓库,可以理解为:系统默认软件源之外,一个专门提供多版本 PHP 的软件源。
若你的服务器默认仓库没有项目需要的 PHP 版本,就可以通过 Remi 选择对应版本,并使用 dnf 安装 PHP-FPM、数据库驱动和其他扩展,无需自己编译源码。
考虑安装速度问题,本文使用Remi的国内镜像阿里云地址,同时epel也使用阿里云加速,先安装升级源:
bash
#安装PHP升级所需的软件仓库源
#1-安装epel源
dnf install -y https://mirrors.aliyun.com/epel/epel-release-latest-$(rpm -E %rhel).noarch.rpm
#2-安装remi源
dnf install -y https://mirrors.aliyun.com/remi/enterprise/remi-release-$(rpm -E %rhel).rpm
bash
#将Remi仓库下载地址修改为阿里云地址,速度更快
#1-先备份
cp -a /etc/yum.repos.d/remi.repo /etc/yum.repos.d/remi.repo.bak
cp -a /etc/yum.repos.d/remi-modular.repo /etc/yum.repos.d/remi-modular.repo.bak
#2-将【/etc/yum.repos.d/remi-modular.repo】文件的内容替换为如下内容:
cat>/etc/yum.repos.d/remi-modular.repo<<'EOF'
# Repository mirror: https://mirrors.aliyun.com/remi/
[remi-modular]
name=Remi's Modular repository for Enterprise Linux $releasever_major - $basearch
baseurl=https://mirrors.aliyun.com/remi/enterprise/$releasever_major/modular/$basearch/
enabled=1
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el$releasever_major
[remi-modular-debuginfo]
name=Remi's Modular repository for Enterprise Linux $releasever_major - $basearch - debuginfo
baseurl=https://mirrors.aliyun.com/remi/enterprise/$releasever_major/debug-modular/$basearch/
enabled=0
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el$releasever_major
[remi-modular-test]
name=Remi's Modular testing repository for Enterprise Linux $releasever_major - $basearch
baseurl=https://mirrors.aliyun.com/remi/enterprise/$releasever_major/modular-test/$basearch/
enabled=0
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el$releasever_major
[remi-modular-test-debuginfo]
name=Remi's Modular testing repository for Enterprise Linux $releasever_major - $basearch - debuginfo
baseurl=https://mirrors.aliyun.com/remi/enterprise/$releasever_major/debug-modular-test/$basearch/
enabled=0
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el$releasever_major
EOF
#2.1-刷新该仓库缓存,并查看 PHP 模块流
dnf --disablerepo='*' --enablerepo=remi-modular makecache --refresh
dnf --disablerepo='*' --enablerepo=remi-modular module list php
#3-将【/etc/yum.repos.d/remi.repo】文件内容替换为如下内容:
sudo tee /etc/yum.repos.d/remi.repo<<'EOF'
# Repository mirror: https://mirrors.aliyun.com/remi/
[remi]
name=Remi's RPM repository for Enterprise Linux $releasever_major - $basearch
baseurl=https://mirrors.aliyun.com/remi/enterprise/$releasever_major/remi/$basearch/
enabled=0
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el$releasever_major
[remi-test]
name=Remi's test RPM repository for Enterprise Linux $releasever_major - $basearch
baseurl=https://mirrors.aliyun.com/remi/enterprise/$releasever_major/test/$basearch/
# If enabled, also enable "remi"
enabled=0
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el$releasever_major
[remi-debuginfo]
name=Remi's RPM repository for Enterprise Linux $releasever_major - $basearch - debuginfo
baseurl=https://mirrors.aliyun.com/remi/enterprise/$releasever_major/debug-remi/$basearch/
enabled=0
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el$releasever_major
[remi-test-debuginfo]
name=Remi's test RPM repository for Enterprise Linux $releasever_major - $basearch - debuginfo
baseurl=https://mirrors.aliyun.com/remi/enterprise/$releasever_major/debug-test/$basearch/
enabled=0
gpgcheck=1
repo_gpgcheck=0
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-remi.el$releasever_major
EOF
#3.1-单独验证 remi 仓库能否正常获取元数据
dnf --disablerepo='*' --enablerepo=remi makecache --refresh
bash
#1-开始启用没有安全漏洞的php版本(如:直接启用php8.4版本)
dnf module reset php -y
dnf module list php
dnf module enable php:remi-8.4 -y
#2-安装新版本PHP
#2.1-强制刷新 yum 源缓存,检查所有 php 开头的包有没有可升级版本,只查看、不执行升级
dnf --refresh check-update 'php*'
#2.2-强制刷新仓库缓存,把本机所有已安装的 php 开头的包(php 主程序、fpm、所有扩展)一起升级到源内最新小版本,自动处理依赖并安装。
dnf upgrade --refresh 'php*'
#2.3-查看安装的软件包是否已经升级到了所需的8.4.27版本
rpm -qa 'php*'
#3-重启对应服务,以使更新生效
/usr/sbin/php-fpm -t
systemctl restart php-fpm
systemctl is-active php-fpm
journalctl -u php-fpm -n 100 --no-pager
rpm -q php-cli php-common php-fpm
#4-升级后验证(对照升级前记录,确认全部旧 Web 工作进程已经退出,并重新启动长期运行的队列等 PHP 进程。CLI 检查只是辅助,FPM 或 Apache 的实际版本必须单独确认)
rpm -qa 'php*'
php -v
php -m
ps -eo pid,ppid,user,lstart,args | grep -E '[p]hp-fpm|[p]hp-cgi|[h]ttpd|[a]pache2'
三、无法立即更新PHP彻底解决此次安全漏洞的缓解方法
若无法立即进行更新:虽然更新是彻底解决问题的办法,但以下措施可以降低受到特定攻击的风险。不过,这些措施并不能确保系统完全安全。
| 针对OOM 漏洞导致的 RCE 攻击(CVE-2026-109618)的缓解方法 |
|---|
1、将 memory_limit=-1 的值设置为一个应用程序无法更改的值(如:在 FPM 进程池或 Apache 配置中设置 php_admin_value )。同时,要确保没有任何进程池、目录或应用程序设置会再次对内存大小进行限制。这样就能避免因 PHP 内存限制而导致的故障。不过,如果内存分配本身出现故障,仍然有可能导致系统出现问题。 |
2、将 post_max_size 的值设置得较低。对于那些不需要上传文件的应用程序,可将 post_max_size = 8M 设置为适当数值。如果必须上传文件,则应将 post_max_size = 32M 设置为适当数值。同时,需确保 upload_max_filesize 的值低于 post_max_size 。建议在 Web 服务器或代理层面上对解码后的请求大小、输入字段数量、多部分数据的数量、头部信息数量以及嵌套深度等参数设置限制。这些限制也适用于那些没有有效 Content-Length 值的请求。 |
3、使用容器或 cgroup 限制作为可用性边界,最好能设置相应的策略,仅终止受影响的 worker 进程。切勿使用 RLIMIT_AS 或 RLIMIT_DATA 作为替代方案,因为这些方式可能导致分配失败。 |
| 通过过时的全局状态实现 RCE 攻击(CVE-2026-109619)的缓解方法 |
|---|
1、将 session.upload_progress.enabled=0 设置为相应值,然后重新启动所有工作进程。请使用 php.ini ,即那种在请求解析之前生效的 FPM 池或服务器配置。不要依赖 ini_set() 或 FPM 的 .user.ini 设置。这样做可以阻止那些已被确认的会话路径,但不会影响其他会话路径。 |
2、在 PHP 8.5 中,需将 fatal_error_backtraces=0 的值设置好,然后重启所有工作进程。这样做只能阻止那些已被确认为致命错误的错误信息被输出。 |
3、移除那些在初始化之前会输出诊断信息的无效或已过时的设置。使用 error_reporting 、 display_errors 或 @ 来隐藏这些诊断信息是没有任何作用的。 |
| 4、修复那些容易被攻击者利用的致命错误以及执行超时问题,包括回调函数出错、上传或导入处理失败以及相关清理操作失败的情况。 |