2026年PHP安全更新:PHP2Shell(CVSS 9.2)修复指南
2026年10月21日,PHP发布了8.5.12、8.4.27、8.3.36和8.2.35,修复了两个可被未经身份验证的请求远程执行代码的漏洞。无论PHP运行在你自己的服务器、客户的服务器,还是你所在代理商的共享主机上,这都是一件"今天就要升级"的事,而不是排进路线图慢慢来的事。
这次披露本身就不同寻常。10月20日,PHP安全团队发布了提前预告,将发布时间定在10月21日14:00 UTC,并保留提前发布的权利。CVE编号在正式披露前一天就已泄露,随之流传的还有关于严重程度和受影响版本的夸大说法。各发行版厂商此前已拿到补丁,各国CERT甚至在代码公开之前就开始发布指引。换句话说:版本一经发布,谁都没有正当理由拖延。
两个漏洞,一次紧急发布
这两个漏洞都不是应用层的逻辑错误,而是出在PHP建立和清理请求的方式上。这正是任何框架更新、插件更新或WAF规则都无法替代解释器升级的原因。
CVE-2026-109618——内存耗尽损坏导致的RCE。PHP在内存耗尽时会中止请求,而不是继续执行。公告描述了这样一条路径:这次中止在内部请求数据只初始化了一部分时打断了PHP,随后的请求清理步骤因此把无效数据当作有效数据来处理。能把内存占用推到极限的攻击者,就可以借此左右接下来的走向。正是这个漏洞让缓解建议显得如此反直觉:厂商要求管理员避免PHP触及其自身的内存上限,而不是收紧上限。
CVE-2026-109619——残留全局状态导致的RCE。属于某个请求的内部状态,可能在其背后的内存被释放之后依然存在。同一个worker进程处理的下一个请求,可能在正常初始化将其重置之前就消费了这段残留状态。对于长生命周期的worker——PHP-FPM、mod_php和LiteSpeed恰恰如此——内存复用是正常且持续存在的状态,因此这并不是只有特殊架构才会踩到的边角案例。
| 编号 | 漏洞性质 | 严重程度 | 修复版本 |
|---|---|---|---|
| CVE-2026-109618 GHSA-w8hw-w9fx-j79w |
内存耗尽处理在请求数据仅部分初始化时中断PHP;清理步骤随后信任无效数据 | CVSS v4.0 9.2 | 8.5.12 · 8.4.27 · 8.3.36 · 8.2.35 |
| CVE-2026-109619 GHSA-6wmf-4rv5-p3rp |
请求私有的状态在内存释放后仍然存活;同一worker上的后续请求在重新初始化之前就会用到它 | CVSS v4.0 9.2 | 8.5.12 · 8.4.27 · 8.3.36 · 8.2.35 |
PHP2Shell并不是PHP官方项目的正式名称,它源自研究人员和安全厂商并沿用至今,所以你会在扫描器输出、主机商公告和CERT通告中看到它,指的正是这两个CVE。
究竟谁真正暴露在风险中
公告说得很直白:所有处理Web请求的PHP安装都面临风险,包括位于nginx或Apache之后的PHP-FPM、使用mod_php的Apache、php-cgi、LiteSpeed和FrankenPHP。它们之间的差别只在于攻击者需要花多少功夫。
| 部署方式 | 风险 | 原因 |
|---|---|---|
| 使用mod_php的Apache | 最高 | 公告描述了一个已被实际演示的利用方式:未经身份验证的请求在任何应用代码运行之前就能触发攻击 |
| PHP-FPM(nginx、Apache代理) | 高 | 长生命周期的worker会复用请求状态;worker通常与站点以同一用户身份运行 |
| LiteSpeed、php-cgi、FrankenPHP | 高 | 公告点名属于影响范围的请求服务配置 |
| 托管型与共享主机 | 因平台而异——需主动询问 | 通常在平台层面打补丁,但你自己看不到;你的任务是拿到书面形式的内置版本和补丁日期 |
| Cron任务与CLI脚本 | 较低,但仍需更新 | 没有公开的请求入口,但它们共用同一个二进制文件,而且往往保存着凭据 |
如果你为自己的贸易业务托管着WordPress、WooCommerce、Laravel、Magento、与Magento配套的ERP系统或任何PHP预订门户,那么即使应用代码完美无瑕,你也属于上表前三行。想更全面地了解运行时究竟该由谁负责,请参阅我们的土耳其IT外包指南。
两分钟查清你的版本
这里的陷阱在于版本号计算。发行版会在PHP 8.3.6或8.2.x上停留多年,把安全修复回移植进同一个可见的版本号,所以仅凭php -v判断不了你是否已受保护。需要核对三件事。
1. 可见的PHP版本,以及实际承接流量的SAPI。
php -i | grep -E 'PHP Version|memory_limit|session.upload_progress.enabled|fatal_error_backtraces'
systemctl list-units --type=service --state=running | grep -Ei 'php|apache|nginx|litespeed'
2. 发行版软件包版本,而不是上游版本。在Debian和Ubuntu上,changelog才是诚实的答案;在RPM系统上则查询软件包的changelog。
zcat /usr/share/doc/php8.3-cli/changelog.Debian.gz | grep -iE 'CVE-2026-10961[89]'
apt-cache policy php8.3-fpm | head -4
# RPM-based hosts
rpm -q php && rpm -q --changelog php | grep -iE 'CVE-2026-10961[89]'
如果已安装版本的changelog里同时提到了这两个CVE,那么即使php -v仍显示8.3.6,你也已经打上了补丁。这就是真实的安全结论与版本号迷信的区别。
3. 正在运行的进程是否加载了打好补丁的二进制文件。软件包更新后若不重启,内存里跑的仍是旧代码。请对比服务启动时间与软件包安装时间:
stat -c '%y %n' /usr/sbin/php-fpm8.3
在cPanel、Plesk、DirectAdmin或托管平台上,对应的做法是查看PHP选择器加主机商自己的安全公告:确认你的站点实际分配到的是内置版本,而不是面板里宣传的最新版本。如果你的主机商给不出补丁日期,就把站点当作未打补丁处理。我们的数字基础设施服务涵盖这项审计以及升级本身;你也可以直接带着你的技术栈联系我们,我们会告诉你我们会检查什么。
修复版本与EOL悬崖
PHP对每个分支提供两年的积极开发支持,再加两年的安全修复支持。正是这一策略让10月21日出现了四个分支同时修复——也正是它让更老的分支自生自灭。
| 分支 | 修复版本 | 本次公告后的状态 |
|---|---|---|
| PHP 8.5 | 8.5.12 | 处于积极支持阶段 |
| PHP 8.4 | 8.4.27 | 处于积极支持阶段 |
| PHP 8.3 | 8.3.36 | 处于安全支持阶段 |
| PHP 8.2 | 8.2.35 | 安全支持将于2026年12月31日结束——现在就规划升级 |
| PHP 8.1及更早版本 | 上游无发布版本 | 仅有厂商回移植补丁;PHP项目为从源码自行编译的用户发布了7.4、8.0和8.1的最小源码回移植补丁 |
公告中还有两点实用提醒。第一,如果你的PHP来自社区仓库而非发行版,该仓库可能跟不上安全窗口:使用广泛的Remi软件包维护者已表示无法当天提供更新,稳妥的做法是使用发行版软件包或从源码编译。第二,如果你还在PHP 8.1或更早版本上,"等主机商处理"实际上是在替客户做安全决策——要么记录在案,要么改变它。
给生产服务器打补丁而不出事故
同小版本的安全更新很少弄垮应用,会出事的是未经测试的重启。下面这套顺序是我们在在线贸易平台上使用的做法,它能在不拿站点冒险的前提下把风险窗口压到很短。
- 先做快照。一份你实际测试过恢复的hypervisor或卷快照能在几分钟内还原,它让打补丁这个决定变得轻松。
- 在预发环境复现版本。相同分支、相同扩展、相同的PHP配置文件,外加一份生产数据副本。跑通下单、询价和支付链路,而不只是首页。
- 把整套软件包作为一个整体升级。升级过的CLI混着旧的FPM二进制,正是"
php -v看着已修复、Web路径却没修复"这种局面的成因:apt-get update && apt-get install --only-upgrade 'php8.3-*',RPM主机则执行dnf update --security 'php*'。 - 重启提供请求服务的进程,然后确认它确实重启了。
systemctl restart php8.3-fpm(或reload Apache/重启LiteSpeed),然后重新检查服务启动时间,并再次执行版本与changelog查询。 - 观察错误日志一小时。OOM消息、新出现的致命错误特征和异常的5xx比例,是下面的缓解设置可能需要调优的信号——而不是推迟升级的理由。
- 留存证据。软件包版本、提到这两个CVE的changelog条目、重启时间戳。客户、审计员和ISO 27001式的控制评审要的正是这些,而保存它们只需十秒钟。
如果升级必须等待——比如经过验证的ERP集成、受发布冻结约束的上线计划、等待客户签核的流程——当天就在它前面加一道补偿控制,而不是敞着端口碰运气。各国CERT的通告针对的正是这种情况:在无法立即打补丁时,把面向互联网的PHP应用与公共网络隔离。
临时缓解措施:缩小窗口,而非关上窗口
PHP公告明确指出不存在完整的缓解措施,更新是唯一安全的选择。请把以下措施当作以天计而非以周计的过渡保护,设置在应用代码无法覆盖的层级,并在设置后重启每一个worker。
针对CVE-2026-109618(内存耗尽路径)
- 让PHP去耗尽整机内存,而不是触发它自身的限制:
memory_limit = -1,最好写成php_admin_value memory_limit = -1,让应用代码无法把上限加回去。 - 在解释器层面保持请求体小巧:公告的指引是,无文件上传时约8 MB,有上传时约32 MB,并保持
upload_max_filesize低于post_max_size。 - 在代理层执行那些代价高昂的限制,因为造成破坏的是解码后的请求而不是线上传输体积:解码后的请求体总大小、输入字段数量、multipart分段数量、头部数量、嵌套深度——并覆盖没有可用
Content-Length就到达的请求。 - 用容器或cgroup内存限制控制爆炸半径,把它设定成"worker被杀只是一次可用性事件,而不是整机事件"的规模。不要用
RLIMIT_AS或RLIMIT_DATA替代:它们触发的恰恰是漏洞路径中的那次分配失败。
针对CVE-2026-109619(残留状态路径)
- 用
session.upload_progress.enabled = 0禁用会话上传进度,设置在php.ini、FPM池或服务器配置里——任何在请求解析之前生效的位置。然后重启每一个worker,因为FPM会在请求之间共享已初始化的模块状态。ini_set()和.user.ini都来不及发挥作用。 - 在PHP 8.5上,设置
fatal_error_backtraces = 0并重启。 - 清理那些无效或已废弃的按请求设置,它们会在早期初始化阶段产生诊断信息。用
error_reporting、display_errors或@压掉报错,并不能移除这条代码路径。 - 排查上传与导入处理器、回调代码和清理例程中攻击者可触发的致命错误和长时间运行的超时——这两个漏洞藏身的正是这些地方。
源站前面的CDN或WAF依然物有所值:限速、拦截异常请求形态和虚拟补丁能降低噪声、拖慢机会主义扫描。但对有针对性的攻击者来说它们只是减速带,不是能作为整改结果签字放行的控制手段。
今天该向你的主机服务商问些什么
客服话术会用"我们始终都打好补丁"来回答这个问题。你应该索要的是可以存档的证据:
- 分配给我们账户的内置PHP版本,以及它来自发行版软件包还是厂商自建的构建。
- CVE-2026-109618和CVE-2026-109619的修复到达你们整个机群的日期,连同内部软件包版本字符串。
- 修复是否覆盖为我们站点提供服务的SAPI(FPM、LiteSpeed、mod_php),以及我们可能用于恢复的预发或备份节点。
- 如果平台尚未打补丁:目前启用了什么补偿控制,将持续到什么时候。
- 你们对EOL分支的一贯政策——2027年1月1日PHP 8.2安全支持停止时,我的站点会怎样。
能在一封邮件里答完这五个问题的服务商值得留住;绕一圈回来全是营销说辞的那家,就该谈谈迁移了。这与我们的ISO 27001差距评估工作遵循同样的尽调原则:交付物是证据,而不是安慰。
是否应当假定自己已被入侵?
不必自动这么认定——但要核查而不是猜测,尤其是当你的服务器在打补丁之前的窗口期暴露在公网上时。
- 查找不是你创建的进程、监听端口和cron条目,以及PHP worker用户发出的对外连接。
- 把可写的应用目录与部署基准做差异比对:新增的PHP文件、被改动过的上传目录、意外变为可执行的临时目录。
- 在Web日志中搜索披露时段前后的请求风暴、异常巨大的multipart请求体,或返回500且在PHP错误日志中留下OOM特征的请求。
- 确认打好补丁的二进制文件确实在运行——上面的重启时间对比就能回答。
- 一旦发现异常,就按安全事件处理:保全磁盘和日志,轮换应用能读到的每一项凭据,并从已知完好的镜像重建。在被入侵的主机上重装PHP软件包不叫整改。
来自我们自家基础设施的一个实例
worldwidetradex.com运行在Ubuntu 24.04 LTS上,采用nginx和PHP-FPM,因此我们在10月22日对自己执行了上述检查。结果很好地说明了为什么版本号计算如此重要:
php -v报告的是PHP 8.3.6——对照上游PHP,这个数字看起来陈旧了三年,相当吓人。
dpkg -l报告的是8.3.6-0ubuntu0.24.04.13,该版本随附的Debian软件包changelog列出了CVE-2026-109618和CVE-2026-109619,且厂商补丁已经应用。apt-cache policy显示已安装版本与候选版本一致,没有更新的版本待装。
结论:通过发行版回移植补丁完成修复,由PHP-FPM而非mod_php提供服务,软件包更新后FPM服务也已重启。同一套检查,五条命令,客户问起时我们随时拿得出证据。
这个经验适用于任何技术栈:你的安全态势等于软件包版本加changelog加重启,而不是主机面板里那条用来宣传的版本号。
让打补丁成为例行公事,而非英雄式救火
PHP2Shell令人印象深刻,不是因为手法罕见,而是因为互联网大半赖以运行的解释器出现了一个身份验证之前即可利用的严重漏洞,成千上万台主机不得不在几小时内做出反应。那些安然睡过这一夜的团队靠的不是运气——他们有自动化。
- 解释器的自动安全升级,并自动记录版本和重启信息。
- 一份清楚掌握每个站点所用SAPI和PHP分支的资产清单,包括预发环境、备份节点,以及2019年留下的那个WordPress。
- 一条演练过的恢复路径,让"立即打补丁"永远不必与"我们无法回滚"放在天平两端。
- 一份分支规划:不让任何站点运行安全支持将在未来十二个月内结束的PHP版本。
- 每台服务器一名指定负责人,并有一条书面化的逐级上报路径,以应对补丁在某个周三14:00 UTC落地的日子。
如果你更愿意购买这种能力而不是自建团队,我们可以接管生产PHP平台——补丁节奏、监控、备份和有据可查的恢复——这是我们托管基础设施工作的一部分。从一份简短的需求说明和数量开始:申请报价。
常见问题
网站运行的是未打补丁的PHP版本,就一定已经被入侵了吗?
不一定。未打补丁且面向互联网的PHP安装确实处于暴露状态,披露前后扫描也已经启动,但暴露不等于失陷。先打补丁,再执行上面的验证步骤;只有在掌握证据后才把主机当作被入侵处理,而且一旦找到证据就重建,而不是清理。
我的主机商说版本是PHP 8.3.6,到底打没打补丁?
取决于构建来源。发行版软件包在保留上游版本号的同时携带CVE修复,所以来自Ubuntu 24.04或Debian的PHP 8.3.6可能早已包含这两个修复。索要完整的软件包版本号,以及写明CVE-2026-109618和CVE-2026-109619的那条changelog记录;在托管平台上则改为索要补丁日期。
更新插件、主题或框架能防住PHP2Shell吗?
不能。两个缺陷都在PHP解释器的请求建立与清理环节,不在应用代码里。保持应用更新仍是好习惯,WAF也能拖慢机会主义流量,但只有升级解释器才能消除漏洞。
把内存限制调低一点,就能挡住内存耗尽漏洞吗?
恰恰相反。公告针对CVE-2026-109618的临时指引,是让PHP不再因自身内存上限而中止——memory_limit = -1,并设置成应用代码无法覆盖——然后在代理层限制请求复杂度,再用容器或cgroup限制约束worker。更紧的PHP限制恰好落在被利用的故障模式之内。
我们还在PHP 8.1或更早版本上,上游没有发布更新,现在怎么办?
有条件就迁移到受支持的分支,这才是长久之计。如果做不到,就从你的发行版或厂商获取修复——这是唯一现实的来源;自行从源码编译的,可以应用PHP项目为7.4、8.0和8.1发布的最小源码回移植补丁。把这项决策记录在案,因为每多保留一个月,你的风险状况都会随之变化。
把PHP 8.3升级到8.3.36,对在线商店有风险吗?
同小版本的安全更新改变的是解释器二进制文件,而不是语言本身,所以大多数应用不受影响。风险在运维层面:软件包只升级了一半、忘了重启,或某个只在真实负载下才会暴露的配置问题。做快照,升级整套PHP软件包,重启请求服务进程并验证——平台复杂时,先在预发环境做一遍。
来源
- PHP——安全公告《严重远程代码执行漏洞,请立即更新》以及2026年10月21日的发布公告,包括其中的缓解指引和受支持版本页面上的支持时间线。
- PHP代码库的GitHub Security Advisories:GHSA-w8hw-w9fx-j79w(CVE-2026-109618)和GHSA-6wmf-4rv5-p3rp(CVE-2026-109619)。
- CERT-EU——安全通告2026-018《PHP中的严重漏洞》,点名了这两个CVE。
- Infosecurity Magazine——《PHP严重漏洞在预定安全补丁发布前泄露》(2026年10月21日)以及《亚马逊在漏洞披露前四天检测到PHP2Shell利用尝试》(2026年10月22日)。
希望有人替你完成升级?无论是补丁实施、版本审计、迁移到托管且持续打补丁的PHP平台,还是周边的基础设施,欢迎联系我们或致电+90 505 679 78 03。