---
title: "PHP2Shell 漏洞成因与修复指南"
description: "解析 PHP2Shell 的两类生命周期缺陷，整理运行时升级、临时缓解、补丁选择与实际 Web 链路验收方法。"
language: "zh-Hans"
url: "https://keendigit.com/zh/insights/php2shell-remediation-guide"
author: "KeenDigit"
published: "2026-10-22"
---

# PHP2Shell 漏洞成因与修复指南

解析 PHP2Shell 的两类生命周期缺陷，整理运行时升级、临时缓解、补丁选择与实际 Web 链路验收方法。

**资料核验截至：2026 年 10 月 22 日**\
**适用对象：PHP 应用运维、安全响应人员，以及维护 PHP 自编译版本的工程师。**

本文整理 PHP2Shell 的漏洞成因、影响范围、正式修复流程、临时缓解措施与官方补丁实现。正文使用 [S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1) 等编号引用；全部来源链接集中列在文末附录。配置片段是部署模板，需结合实际版本、业务容量和配置继承关系调整。

## TL;DR：修复步骤与建议

1. **升级 PHP 运行时**：对应分支至少升级至 **8.2.35、8.3.36、8.4.27 或 8.5.12**；使用发行版软件包时，以厂商确认同时修复两个 CVE 的完整包版本为准。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)[S10](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-10)
2. **替换全部旧进程**：升级后重启或滚动替换所有 PHP worker、承载 PHP 的服务及容器，确认旧实例已退出。
3. **从实际 Web 链路验收**：核对 Web SAPI、完整软件包版本和运行实例；仅检查 `php -v` 不足以证明线上已修复。
4. **做业务回归并恢复正常预算**：检查登录、session、上传、导入及错误处理；若应急时取消过 PHP 内存上限，修复后恢复适当的有限预算。
5. **暂时无法升级时**：优先在 PHP 之前限制不可信流量；按第 5 节评估关闭 upload progress、PHP 8.5 fatal backtrace 等措施。所有配置缓解和 WAF 均有覆盖边界，不能替代升级。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

## 1. 漏洞概览与风险判断

PHP2Shell 在本文中指 2026 年 10 月 21 日 PHP 官方披露的两项 Critical 级别漏洞。两者都与 PHP 引擎的内存和请求生命周期管理有关，存在未认证远程代码执行风险。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

| 项目          | CVE-2026-109618         | CVE-2026-109619            |
| ----------- | ----------------------- | -------------------------- |
| 官方名称        | RCE via OOM corruption  | RCE via stale global state |
| 核心问题        | 内存不足导致异常跳转，留下尚未完成初始化的状态 | worker 中残留指向已释放请求内存的全局状态   |
| 典型发生时机      | 当前请求的初始化、执行或清理过程        | 前一请求留下状态，后续请求提前消费          |
| OOM 是否为必要条件 | 属于漏洞核心触发机制              | 不是；其他致命错误及清理后期诊断也可能参与      |
| 根本修复方向      | 安全处理分配失败与请求终止           | 修正状态所有权、清理时机与提前初始化         |
| 处置要求        | 必须修复                    | 必须同时修复                     |

依据：两份官方安全公告及修复提交。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)[S4](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-4)

### 1.1 受影响部署应如何理解

PHP 官方将**所有处理 Web 请求的 PHP 部署**列入风险范围，包括 PHP-FPM、Apache mod\_php、php-cgi、FrankenPHP 和 LiteSpeed。已演示的 mod\_php 攻击可以发生在应用代码执行之前，因此仅依赖 PHP 应用中的登录检查、框架中间件或上传校验，并不能覆盖这种路径。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)

需要区分“部署需要修复”与“某一攻击链已被完整证明”。CVE-2026-109618 的公告说明：FPM 在请求启动失败后会退出子进程，阻断了 mod\_php 对应的启动失败后清理路径；但启动成功后的 OOM 路径仍存在风险。该公告同时注明，针对这一 CVE 的完整 FPM 利用尚未演示。这个限制不能据此推导出 FPM 安全，尤其不能忽略 CVE-2026-109619。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

成功利用的直接权限边界取决于承载 PHP 的进程身份及其可访问资源。通常应关注网站代码、上传目录、数据库凭据、服务令牌和内部网络访问能力；“远程代码执行”不等于天然获得 root 权限。这里的后果判断基于进程权限模型，实际影响需要结合部署权限评估。

普通一次性 CLI 脚本不具备同样的 HTTP 入口；但以 CLI 或嵌入方式长期处理外部任务的进程，仍应纳入运行时与生命周期盘点。只更新 CMS、框架或 Composer 依赖，也不会自动替换底层 PHP 引擎。

## 2. 成因分析

### 2.1 先理解两个生命周期

PHP worker 往往连续处理多个请求。请求期间分配的内存可以在请求结束时统一释放，而 worker 本身及其部分全局字段继续存在。

因此，引擎必须维持两项约束：

- **初始化约束**：对其他代码可见的数据，必须处于可安全读取、遍历和销毁的状态。
- **生命周期约束**：请求内存释放后，存活更久的字段不能继续把其中的地址当成有效状态。

这两项约束分别对应两组漏洞的主要失败方式。以下流程是结合公告与补丁整理的机制说明，不代表每种部署都存在完全相同的可达路径。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)

### 2.2 CVE-2026-109618：OOM 打断了尚未完成的状态更新

旧实现使用 `longjmp()` 一类非局部跳转来处理 OOM，相关路径通过 `zend_bailout()` 退出当前执行上下文。危险在于，一段 C 代码可能已经将新对象、数组或请求状态发布给其他代码，但还未完成全部字段初始化；此时任意一次相关时机上的分配失败，都可能使正常的收尾步骤被跳过。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)

可以把失败过程拆成四步：

1. 创建或更新请求内部结构，并使其变得可访问。
2. 后续内存分配失败，触发 OOM。
3. 执行流跳离当前函数，原本用于完成初始化或恢复状态的代码没有执行。
4. 请求清理继续处理这些结构，将无效内容当成有效字段或地址。

这里的关键不是“内存用完就一定产生 RCE”，而是**内存不足改变了控制流，破坏了引擎对结构有效性的假设**。失败的分配也不必与最终被错误读取的结构直接相关。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)

#### 为什么较小的请求也可能耗尽内存

请求体字节数与解析后的堆占用不是一回事。一个字段在 PHP 内部可能对应多个字符串、数组元素和元数据对象；输入的结构复杂度会影响内存放大程度。

官方公告列出的 multipart 问题包括：缺少可用声明长度时的初始大小检查缺口、单个 part 的头部数量未受到充分限制，以及空文件控件仍产生元数据、嵌套名称进一步增加数组工作量。应用自身对输入的展开、解码或重复分配，也可能成为 OOM 来源。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)

由此可得两条防护结论：

- 请求体限制必须尽量在进入 PHP 前执行，并考虑实际读取长度与结构复杂度。
- 单独限制文件大小、文件数量，或只关闭一个上传入口，无法覆盖所有 OOM 来源。

#### 为什么应用鉴权可能来不及

在 mod\_php 中，请求初始化阶段就可能自动解析请求体；即使启动失败，相关请求清理仍会执行。已确认攻击路径因此可以先于应用代码发生。若要在这个阶段减少暴露，限制需要放在反向代理、Web 服务器或其他位于 PHP 请求处理之前的边界。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)

### 2.3 CVE-2026-109619：请求内存已释放，全局引用仍然存活

这一问题涉及错误、session、对象或扩展等状态：字段保存在 worker 可长期访问的位置，字段引用的数据却属于某一次请求。只要引用在请求堆释放前未被正确清理，后续请求就可能遇到悬空状态，形成 use-after-free。[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

官方分析将可利用条件分为“生产者”和“消费者”：

| 角色或条件     | 含义                     | 对防护的启示                     |
| --------- | ---------------------- | -------------------------- |
| 状态生产者     | 把本请求拥有的状态放入稍后仍可访问的位置   | 要审视异常退出后是否完成对应清理           |
| 状态残留      | 没有清空，或清空后又被晚期操作重新填入    | 仅在较早的 shutdown 阶段清空一次可能不够  |
| worker 复用 | 同一 worker 继续处理后续请求     | 多 worker 只是调度差异，不是内存安全保证   |
| 提前消费者     | 在对应状态正常初始化前读取、释放或判断残留值 | 必须检查初始化之前的 INI、session 等路径 |

这些条件需要组合成立；任意一次错误日志本身并不证明漏洞已经被成功利用。[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

#### 它不以 OOM 为必要前提

致命错误可能使成对的状态发布与恢复过程被打断。例如编译或解析失败、未捕获错误、`require` 失败及执行超时，都可能参与特定状态的残留。清理后期的非致命诊断还可能重新填入已经清空过的错误信息。[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

因此，将 `memory_limit` 改为无限制，不能覆盖这组漏洞。

#### “初始化之前”需要分别看待

两类已确认消费者的时序不同：

- **早期 INI 诊断**：某些按请求应用的配置在普通 executor 初始化前发出 warning、notice 或 deprecation，诊断处理可能访问旧错误状态。
- **session upload progress**：请求体解析过程中可能提前处理 session，而常规模块 `RINIT` 尚未完成；不能以应用没有显式调用 `session_start()` 推断该路径不存在。[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

并非所有 INI 配置都具有相同风险时机。mod\_php 的目录级配置与 FPM 前端传入的 `PHP_VALUE` / `PHP_ADMIN_VALUE`，需要与普通 `php.ini`、FPM pool 启动配置及 FPM `.user.ini` 区分。后几者的应用时机不同，不能一概称为同一种早期消费者。[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

关闭 `display_errors`、`log_errors`，降低 `error_reporting`，或者使用 `@`，只影响诊断的可见性；相关内部状态可能在决定是否输出日志之前已经被访问。[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

## 3. 修复版本与补丁选择

### 3.1 上游正式修复基线

| 当前 PHP 分支 | 首个包含本次正式修复的版本 | 建议                           |
| --------- | ------------- | ---------------------------- |
| 8.2       | 8.2.35        | 升级至该版本或包含相同修复的后续版本           |
| 8.3       | 8.3.36        | 同上                           |
| 8.4       | 8.4.27        | 同上                           |
| 8.5       | 8.5.12        | 同上                           |
| 8.1 及更早版本 | 上游常规支持已结束     | 优先迁移至受支持分支；短期核对发行版或 LTS 厂商回补 |

版本与支持状态依据 PHP 官方公告，时间基线为 2026 年 10 月 22 日。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)

这些是**分支内的修复门槛**，不能用一句“所有低于 8.5.12 的版本均未修复”替代。8.2.35 与 8.4.27 等其他分支版本已经包含修复。

### 3.2 发行版版本号不能只看上游部分

发行版常在较旧的上游版本号基础上回补安全修复。验收应同时检查发行版、仓库来源、完整包版本和安全公告。

例如，Ubuntu USN-8948-1 列出的 Ubuntu 24.04 LTS 修复包版本为：

```text
8.3.6-0ubuntu0.24.04.13
```

它的上游部分仍是 `8.3.6`，但该发行版完整包版本已列入本次修复公告。这一结论只适用于对应发行版和包来源，不能推广到任意同样显示 `8.3.6` 的构建。[S10](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-10)

### 3.3 实际应选择哪一种 patch

| 场景               | 优先方案                 | 验收重点                 |
| ---------------- | -------------------- | -------------------- |
| 使用发行版 PHP 包      | 安装厂商发布的安全更新          | 公告同时覆盖两个 CVE；完整包版本正确 |
| 使用官方 PHP 容器或自建镜像 | 更新基础运行时，重新构建并部署      | 运行实例使用新镜像；旧副本全部退出    |
| 自编译受支持分支         | 使用该分支正式安全版本或完整官方补丁集  | 分支匹配、依赖补齐、扩展兼容与回归通过  |
| 受制于 7.4、8.0、8.1  | 优先厂商回补；必要时评估官方应急源码分支 | 明确行为变化，并安排迁移至受支持版本   |

官方已发布补丁，实际修复应从这些补丁出发。第 6 节中的代码片段用于说明设计与审阅重点，不构成可单独应用的完整补丁。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)[S4](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-4)[S6](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-6)

## 4. 正式修复流程

### 4.1 盘点真正处理请求的运行时

建议形成一张简短台账，至少记录：

- 入口域名或服务、反向代理后端、PHP SAPI。
- PHP 分支、完整软件包版本或镜像摘要、构建来源。
- FPM pool、实际服务单元、容器或 Pod、副本数量。
- 已加载扩展、主配置与额外配置文件。
- 是否存在其他 PHP 版本、旧部署副本或可以直接访问的源站入口。

这些是实施建议，目的是使“已升级”能对应到每个实际流量入口。特别检查命令行 PHP 与 Web PHP 使用不同版本、镜像构建完成但旧容器仍运行、部分 pool 或副本未替换等情况。

### 4.2 选择并部署正确更新

按第 3 节确认修复版本，优先使用既有可信包源或受控构建链。更新范围应包含实际承载请求的组件，例如 FPM 包、Apache PHP 模块、CGI 二进制，或将 PHP 嵌入其中的服务构建。

升级前保留配置与包清单，在与生产相近的环境先验证启动和关键业务。对于自行维护的扩展，检查对应 PHP 版本的构建与运行兼容性。只从另一个分支挑取一个看似相关的提交，不能保证整个修复集已完整落地。

### 4.3 重启或滚动替换运行实例

安装文件与进程加载的新代码是两件事。应重启对应服务，或完成滚动替换，并确认旧 worker 与旧容器已经退出。

对于 mod\_php，重启对象通常是承载模块的 Apache 进程；对于嵌入 PHP 的服务，需要替换承载进程本身。对 FPM 而言，清理 OPcache 不能替代运行时更新与旧 worker 退出。

采用优雅切换时，需要检查旧代进程是否仍处理长连接或未完成请求。验收记录应包含实际服务、部署版本、变更完成时间及旧实例退出结果。

### 4.4 从真实 Web 请求验证结果

推荐把下面几项证据组合起来，而不是仅依赖单条版本命令：

| 证据             | 能证明什么             | 不能单独证明什么        |
| -------------- | ----------------- | --------------- |
| 包管理器或镜像元数据     | 安装来源与完整构建版本       | 流量已经切换到该实例      |
| 进程及部署状态        | worker 或容器完成替换    | 对外入口一定路由到它      |
| 经真实入口访问的受限诊断结果 | 实际 Web SAPI 和运行配置 | 发行版回补是否覆盖两个 CVE |
| 厂商公告或完整补丁记录    | 本次漏洞修复包含在该构建中     | 所有副本均已部署        |

可通过已有内部诊断能力，或临时且受访问控制的诊断入口，核对 `PHP_VERSION`、`PHP_SAPI`、加载的配置文件及应急配置。确保请求没有被 CDN 或页面缓存直接返回；完成后移除临时诊断入口。

`php -v` 通常只说明当前 shell 选择的 CLI 二进制；`php-fpm -i` 等命令也不等于对现存、正在接收流量的 worker 做了验证。不要将完整 `phpinfo()` 页面长期暴露到公网。

### 4.5 业务回归与应急配置退出

围绕本次改动影响的功能验证正常登录、session 读写、表单与上传、导入、错误处理和 worker 稳定性。检查错误率、响应延迟、内存峰值、进程退出及重建频率是否异常。

若临时设置了 `memory_limit=-1`，在正式补丁生效后恢复与业务容量匹配的有限预算；若设置过 `pm.max_requests=1`，在确认修复后恢复经过容量评估的生命周期配置。输入大小限制和来源访问控制可按业务需要保留。

若升级出现兼容性问题，应优先暂停受影响入口、限制其外部访问或调整应用，再处理回退。直接让未修复运行时重新对公网服务，会重新打开原有风险。

## 5. 无法立即升级时的缓解措施

本节用于短期降低暴露或改善可用性。每项措施只覆盖特定路径，多个措施组合也不等价于完整修复。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

### 5.1 优先控制进入 PHP 的流量

如果业务允许，将敏感服务临时限制为可信来源，或在 PHP 之前执行访问控制。需要上传的业务收紧请求体、字段、part、part 内头部数量及嵌套深度；不需要 multipart 的业务可临时在前端拒绝这类请求。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)

这些限制要覆盖所有通往 PHP 的入口。仅拦截名为“上传”的 URL，会漏掉其他仍由 PHP 自动解析请求体的入口。对于分块传输、缺少可用声明长度等情况，应以实际读取量执行限制；应用再次解压或展开内容时，还要有自己的输出大小预算。

### 5.2 FPM pool 配置模板

以下内容放在**实际生效的 FPM pool 配置段**中，不能原样放入 `php.ini`。这里的 8M 和 4M 是演示值，应根据业务调整；限制整个 POST 请求时还要为表单字段与 multipart 边界保留空间。[S11](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-11)[S12](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-12)

```ini
; Temporary mitigation: evaluate capacity before applying.
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 4M
php_admin_flag[session.upload_progress.enabled] = off
```

**仅 PHP 8.5**，另加：

```ini
php_admin_flag[fatal_error_backtraces] = off
```

各项作用与边界如下：

| 配置                                    | 预期作用                  | 必须保留的限制说明                        |
| ------------------------------------- | --------------------- | -------------------------------- |
| `memory_limit=-1`                     | 去掉可预测的 PHP 请求内存上限触发条件 | 不防底层分配失败；取消了原有预算，可能增加内存耗尽和服务中断风险 |
| 较小的 `post_max_size`                   | 缩小进入解析流程的数据规模         | 未修复解析器有已知缺口；不能单独阻止内存放大           |
| `upload_max_filesize < post_max_size` | 限制单文件大小并保留请求开销空间      | 不约束全部字段、元数据或应用内分配                |
| 关闭 upload progress                    | 阻断已确认的 session 相关路径   | 不覆盖其他全局状态变体                      |
| PHP 8.5 关闭 fatal backtrace            | 阻断已确认的相关状态生产者         | 不覆盖 session 等其他路径                |

措施边界来自官方公告；pool 语法与管理级配置语义见 PHP 手册。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)[S11](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-11)

管理级 `php_admin_*` 设置不能由应用通过 `ini_set()` 改写。仍应审查所有 pool、目录级与前端传入配置，确认实际请求上的值符合预期。修改后重建全部 worker，避免已有进程继续持有旧状态。

如果选择直接修改 `php.ini`，语法应改为：

```ini
memory_limit = -1
post_max_size = 8M
upload_max_filesize = 4M
session.upload_progress.enabled = Off
```

与 pool 中的 `php_admin_value` 不同，仅在 `php.ini` 设置 `memory_limit=-1` 不能阻止应用再次修改它。应检查框架、应用代码及其他配置是否重新设置有限值；需要强制执行时，使用适合当前 SAPI 的管理级设置，并核对实际生效值。[S11](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-11)[S12](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-12)

PHP 8.5 才追加 `fatal_error_backtraces = Off`。不要在应用代码中使用 `ini_set()` 作为关闭 upload progress 的补救，也不要依赖 FPM `.user.ini` 来保证它在请求解析前生效。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)

### 5.3 取消 PHP 内存上限时如何控制代价

`memory_limit=-1` 是未修复环境的临时路径缓解，不应作为长期默认配置照搬。应用或请求失控时，它可能耗尽更多宿主机内存。因此，使用前应评估并发数、单 worker 峰值及服务总容量，同时部署前端输入限制。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)

容器或 cgroup 内存限制可以提供可用性边界，但不能保证分配永不失败；限制触发时，还可能终止整个容器、master 和全部 worker。隔离粒度及终止策略需要结合部署评估，不能将其称为这两个 CVE 的完整防护。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)

不要以 `RLIMIT_AS`、`RLIMIT_DATA` 或对应的进程地址空间限制替代上述策略；它们可能直接造成底层分配失败，进入仍然有问题的 OOM 路径。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)

### 5.4 Nginx 配置示例

适用于 Nginx 向 PHP-FPM 转发请求的部署。可在实际承载服务的配置层级设置：

```nginx
client_max_body_size 8m;
fastcgi_request_buffering on;
```

`client_max_body_size` 限制请求体大小；`fastcgi_request_buffering on` 让 Nginx 先读取完整请求体，再向 FastCGI 后端发送。是否在某个 `server` 或 `location` 中生效，需要结合已有继承和覆盖关系检查。[S13](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-13)[S14](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-14)

**仅在业务完全不需要 multipart 时**，可在对应服务入口追加拒绝规则：

```nginx
if ($http_content_type ~* "multipart/form-data") {
    return 403;
}
```

这是使用 `if` 和 `return` 的简单入口限制模板，不是解析器漏洞的通用过滤规则。部署前验证配置语法与正常请求，并检查自定义 `error_page` 是否把 403 请求重新交给 PHP。字符串匹配对所有解析差异不提供完整保证。[S15](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-15)

此外，这些配置并不自动限制应用自行解压后的数据量、字段数量或 PHP 对象数；需要上传的业务应使用能够理解其请求结构的额外限制。

### 5.5 可选措施：FPM 每个 worker 只处理一次请求

对能承受开销的 FPM 部署，可评估短期设置：

```ini
pm.max_requests = 1
```

并重建已有 worker。它在 worker 复用层面阻断已描述的跨请求链，但会增加进程创建和初始化开销；同时不能修复 CVE-2026-109618 的同请求 OOM 与清理问题。值较小但大于 1 时，仍然存在请求复用窗口。[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)[S11](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-11)

### 5.6 WAF 与 Cloudflare 的适用边界

Cloudflare 已公布针对 PHP2Shell 的防护，包括 Free 计划在内的部署安排。使用其服务时，应核对实际流量是否经过代理、防护规则是否生效，以及源站能否被直接绕过；付费计划还需检查相应 Managed Rules 的配置与例外。[S16](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-16)

该厂商说明的部分检测利用响应信号，因此不能将所有防护理解成“每次攻击都在到达 PHP 之前被拦截”。WAF 可辅助减少暴露并提供观察信息，但继续升级 PHP 仍是必要工作。[S16](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-16)

### 5.7 常见措施为什么不足

| 措施或判断                                    | 不足之处                                       |
| ---------------------------------------- | ------------------------------------------ |
| 只关闭 `file_uploads`                       | 不能覆盖全部 multipart 处理和应用自身的 OOM 路径           |
| 设置 `enable_post_data_reading=Off`        | 改变自动填充 POST/FILES 的行为；请求体仍可由应用读取，还可能破坏正常业务 |
| 关闭错误显示、日志或使用 `@`                         | 内部诊断处理仍可能访问悬空状态                            |
| 增加 worker 数量                             | 不建立安全的状态清理机制，也不消除单 worker 复用               |
| 仅配置 `disable_functions` 或 `open_basedir` | 这些限制不能修复 C 引擎层内存破坏                         |
| 只检查一次版本或单个副本                             | 不能证明所有入口与运行实例都已替换                          |
| 没有看到异常日志                                 | 不能证明漏洞不存在，也不能证明此前未被利用                      |

其中 PHP 配置与漏洞机制依据见 [S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)[S3](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-3)[S12](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-12)；关于覆盖、验收和权限边界的判断是基于这些机制提出的实施建议。

## 6. 官方 patch 的实现思路与审阅重点

### 6.1 应使用完整、匹配分支的修复

PHP 8.5 的代表性官方提交为：

```text
31c0ea7d7ff1d1fccb6492e52dab544011628065
Avoid state corruption via bailout
```

提交明确关联两个 CVE，既调整 OOM 退出机制，也修复可能在初始化阶段访问的全局状态。提交页面与完整 diff 见 [S4](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-4)[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)。其他分支应使用自己的正式安全发布或匹配补丁，不能假设 8.5 的改动可直接应用到任意旧版本。

### 6.2 OOM 修复：先恢复可处理状态，再终止请求

正式实现引入了软、硬两个层次的 OOM 处理：达到软限制时记录待处理状态，并在 VM 等安全位置进行受控退出；可失败分配接口则允许调用方显式处理失败。对于不能直接返回失败的分配，若预计超过更高的硬性阈值，便直接终止进程，避免在不安全的中间状态继续处理请求。[S2](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-2)[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)

以下是 8.5 diff 中用于标记待处理 OOM 的关键代码节选：

```c
EG(delayed_oom).state = ZEND_OOM_STATE_PENDING;
zend_atomic_bool_store_ex(&EG(vm_interrupt), true);
```

VM 中断处理及其他安全位置随后调用 `zend_handle_delayed_oom()`。其用户代码执行上下文使用受控的 unwind 路径；硬 OOM 路径调用 `_exit(137)`。[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)

从设计上看，重要变化是：**避免在结构正处于中间状态时随意跨越 C 调用栈，把退出放在能够维护状态约束的位置**。软限制触发后可能需要额外内存完成这一过程，因此不能把新的软阈值理解为进程总内存的绝对上限。

也不能概括成“PHP 已删除所有 `zend_bailout()`”。补丁仍在特定上下文使用相关机制；修复的是危险的退出时机及与之配套的状态管理。[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)

### 6.3 分配调用者与 multipart 解析必须一起修改

如果只把一个分配函数改成“失败返回 NULL”，而原来的调用者仍假定永远返回有效指针，就会引入新的空指针访问或未完成清理。补丁中的可失败分配接口，需要与调用方检查及异常传播共同审阅。[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)

multipart 相关修复还包括按实际读取量执行累计检查、减少无用或重复头部的处理，并在 OOM 待处理状态下采用一致的退出与清理逻辑。这些改动既缩小资源放大面，也使解析器遵守新的 OOM 协议。[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)

对维护自有分支的团队，建议把以下问题作为代码审阅重点：

- 状态在完全初始化前是否已对外可见？
- 分配失败或回调异常后，所有权是否仍然唯一且明确？
- 中途失败能否到达统一清理出口？
- 清理函数能否识别未初始化或已释放字段？
- 解析预算是否以实际处理量为依据，而不仅信任请求声明？
- 新的失败返回值是否已被所有调用者处理？

这些是依据修复机制归纳的审阅问题，并非对某个未提供代码库已经完成的审计结论。

### 6.4 跨请求修复：初始化更早，最终清理更晚

对应 CVE-2026-109619，补丁同时调整多个生命周期边界。下面列出的文件和操作以 PHP 8.5 官方 diff 为例。[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)

| 位置                          | 改动方向                                     | 解决的问题                |
| --------------------------- | ---------------------------------------- | -------------------- |
| `Zend/zend_execute_API.c` 等 | 复位错误、异常、handler、对象等执行器状态                 | 请求结束后仍存在可被下一请求访问的旧引用 |
| `main/main.c` 的启动路径         | 在 SAPI 激活前调用 `php_basic_early_rinit()`   | 提前消费者发生时，相关基础状态尚未初始化 |
| `main/main.c` 的关闭路径         | 销毁 HTTP globals 后标记为未定义                  | 释放后的值仍被当成有效对象        |
| 请求堆最终释放之前                   | 再次清理最后错误信息                               | 较晚的清理诊断重新填入错误状态      |
| `ext/session/session.c`     | upload progress 早期路径先初始化 session globals | 常规模块 RINIT 之前访问旧状态   |
| 序列化相关代码                     | 早期处理中限制类解析与用户回调                          | 初始化未完成时执行额外用户代码      |

例如，启动顺序中加入了：

```c
zend_activate();
php_basic_early_rinit();
sapi_activate();
```

HTTP globals 销毁后补上状态失效标记：

```c
zval_ptr_dtor(&PG(http_globals)[i]);
ZVAL_UNDEF(&PG(http_globals)[i]);
```

这些节选表达了两项通用原则：**第一次可能使用某类状态之前就要初始化；最后一次可能重新产生该状态之后才进行最终清理。** 只在常规 RINIT 或较早的 shutdown 中修一次指针，可能遗漏更早或更晚的访问。[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)

### 6.5 为什么不建议自制“一行修复”

把所有 `zend_bailout()` 替换成 `_exit()`，或者仅在某个入口加一个空指针检查，都会跨越大量不同的执行上下文。它们可能遗漏其他状态字段，也可能改变析构、扩展回调、进程宿主和请求结束语义。

对于受支持版本，优先采用已经发布的完整修复。自有分支的移植应记录补丁来源、适用基线、冲突处理和行为变化，并使用对应上游回归用例及本业务流程验证。这里的建议来自对补丁跨 allocator、VM、session 与请求生命周期改动范围的分析。[S4](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-4)[S5](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-5)

## 7. PHP 7.4、8.0、8.1 的应急回补

PHP 官方针对严重性提供了三个旧分支的最小源码回补，分支名分别为：

```text
PHP-7.4-CVE-2026-109618-109619
PHP-8.0-CVE-2026-109618-109619
PHP-8.1-CVE-2026-109618-109619
```

分支链接见 [S7](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-7)[S8](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-8)[S9](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-9)。这些是针对本次漏洞的应急交付，不表示上游恢复对旧分支的常规支持，也不替代其他安全更新。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)

### 7.1 以 PHP 8.1 回补为例

PHP 8.1 的相关提交是：

```text
1d7dec178a094e6e75b229ce22754045f8c6070a
```

其策略更偏向快速失败：在相应 OOM 路径中输出诊断后调用 `_exit(1)`，并包含全局状态及其他相关修正；同时在内部禁用 session upload progress 的相应处理。相关源码提交见 [S6](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-6)。

这与受支持分支中较完整的延迟 OOM 设计存在行为差异。不能仅因为配置里仍显示 upload progress 开启，就认为经过该回补的内部处理行为也保持不变。

### 7.2 部署时需要接受的代价

直接退出进程可能使当前请求失败，跳过正常 PHP shutdown 回调及部分应用收尾；若宿主采用线程模型，还需评估整个宿主进程退出对其他并发工作造成的影响。进程管理器是否能及时拉起替代进程，也应纳入验证。

因此，旧版本用户应按顺序考虑：

1. 迁移至受支持 PHP 分支及其修复版本。
2. 使用明确覆盖两个 CVE 的发行版或 LTS 厂商更新。
3. 确实受制于旧版源码时，采用匹配版本的官方应急回补，验证完整改动与兼容性，并安排后续迁移。

这里的“最小回补”描述的是官方选择的兼容性与实现范围，不应自动解释为“已知不完整的漏洞修复”。应按对应分支源码和厂商公告判断，不自行删减其中的配套改动。[S1](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-1)[S6](https://keendigit.com/zh/insights/php2shell-remediation-guide#source-6)

## 8. 验收标准与后续检查

建议以以下结果作为本次修复完成的依据：

| 验收项    | 完成标准                                          |
| ------ | --------------------------------------------- |
| 漏洞覆盖   | 构建或厂商公告明确修复 CVE-2026-109618 和 CVE-2026-109619 |
| 部署覆盖   | 全部入口、pool、服务及副本使用修复后的运行时                      |
| 进程生效   | 旧 worker、旧宿主进程和旧容器全部退出                        |
| 真实请求验证 | 经实际 Web 链路确认 SAPI、构建和配置，排除缓存误导                |
| 业务可用性  | session、上传、导入、错误处理等关键功能通过正常回归                 |
| 临时措施收尾 | 无限内存及极端 worker 回收配置已按容量计划恢复或有明确保留原因           |
| 变更证据   | 记录包版本或镜像摘要、更新范围、完成时间与验证结果                     |

上述为实施建议。若需验证 OOM 行为、异常退出或自定义补丁，应在隔离环境中使用上游回归测试或受控故障注入，避免以生产环境反复制造内存耗尽作为验收方式。

同时检查更新前后的 PHP fatal、worker 崩溃、异常重启、请求错误率及 WAF 事件。日志只能提供调查线索：出现 OOM 不等于已经被利用，缺少告警也不能证明未发生过利用。升级修复的是未来可达的漏洞路径；如已发现文件篡改或凭据泄露等异常，应另行开展事件调查。

## 附录：引用来源与链接

所有链接均集中在本附录。漏洞公告、补丁和厂商通告以 **2026 年 10 月 22 日** 的核验结果为基线；后续部署应继续查看供应商更新。正文 \[Sx] 对应以下编号。

### S1 — PHP 官方安全公告与修复版本

- 来源：PHP 项目，2026 年 10 月 21 日。
- 用途：风险范围、四个正式修复版本、官方临时措施及旧分支应急回补说明。
- 链接：[PHP 2026 年公告归档中的本次安全公告](https://www.php.net/archive/2026.php#2026-10-21-5)

### S2 — CVE-2026-109618 官方技术公告

- 来源：php/php-src GitHub Security Advisory，2026 年 10 月 21 日。
- 标识：GHSA-w8hw-w9fx-j79w。
- 用途：OOM、multipart 放大、SAPI 差异与缓解措施的覆盖边界。
- 链接：[RCE via OOM corruption](https://github.com/php/php-src/security/advisories/GHSA-w8hw-w9fx-j79w)

### S3 — CVE-2026-109619 官方技术公告

- 来源：php/php-src GitHub Security Advisory，2026 年 10 月 21 日。
- 标识：GHSA-6wmf-4rv5-p3rp。
- 用途：跨请求悬空状态、生产者与消费者、提前初始化、配置缓解。
- 链接：[RCE via stale global state](https://github.com/php/php-src/security/advisories/GHSA-6wmf-4rv5-p3rp)

### S4 — PHP 8.5 官方修复提交

- 来源：php/php-src。
- 提交：`31c0ea7d7ff1d1fccb6492e52dab544011628065`。
- 用途：确认正式提交同时修复两项漏洞，并说明整体改动方向。
- 链接：[Avoid state corruption via bailout](https://github.com/php/php-src/commit/31c0ea7d7ff1d1fccb6492e52dab544011628065)

### S5 — PHP 8.5 官方修复完整 diff

- 来源：同一官方提交的 diff。
- 用途：核对 allocator、VM、HTTP globals、session、序列化和请求生命周期的具体实现。
- 链接：[官方完整 diff](https://github.com/php/php-src/commit/31c0ea7d7ff1d1fccb6492e52dab544011628065.diff)

### S6 — PHP 8.1 官方应急回补提交

- 来源：php/php-src。
- 提交：`1d7dec178a094e6e75b229ce22754045f8c6070a`。
- 用途：核对旧分支快速退出及 upload progress 行为变化。
- 链接：[PHP 8.1 应急回补提交](https://github.com/php/php-src/commit/1d7dec178a094e6e75b229ce22754045f8c6070a)
- 链接：[对应完整 diff](https://github.com/php/php-src/commit/1d7dec178a094e6e75b229ce22754045f8c6070a.diff)

### S7 — PHP 7.4 官方应急回补分支

- 来源：php/php-src，PHP 官方安全公告所列源码分支。
- 链接：[PHP-7.4-CVE-2026-109618-109619](https://github.com/php/php-src/tree/PHP-7.4-CVE-2026-109618-109619)

### S8 — PHP 8.0 官方应急回补分支

- 来源：php/php-src，PHP 官方安全公告所列源码分支。
- 链接：[PHP-8.0-CVE-2026-109618-109619](https://github.com/php/php-src/tree/PHP-8.0-CVE-2026-109618-109619)

### S9 — PHP 8.1 官方应急回补分支

- 来源：php/php-src，PHP 官方安全公告所列源码分支。
- 链接：[PHP-8.1-CVE-2026-109618-109619](https://github.com/php/php-src/tree/PHP-8.1-CVE-2026-109618-109619)

### S10 — Ubuntu 安全公告 USN-8948-1

- 来源：Canonical / Ubuntu，2026 年 10 月 21 日。
- 用途：发行版回补及完整软件包版本示例。
- 链接：[USN-8948-1: PHP vulnerabilities](https://ubuntu.com/security/notices/USN-8948-1)

### S11 — PHP-FPM 配置手册

- 来源：PHP 官方文档。
- 用途：`php_admin_value`、`php_admin_flag`、`pm.max_requests` 等配置语义。
- 链接：[PHP-FPM Configuration](https://www.php.net/manual/en/install.fpm.configuration.php)

### S12 — PHP 核心 INI 配置手册

- 来源：PHP 官方文档。
- 用途：请求体、上传、内存与自动 POST 读取配置的定义和适用范围。
- 链接：[Description of core php.ini directives](https://www.php.net/manual/en/ini.core.php)

### S13 — Nginx 请求体大小限制

- 来源：Nginx 官方文档。
- 用途：`client_max_body_size` 的含义及可用配置层级。
- 链接：[ngx\_http\_core\_module / client\_max\_body\_size](https://nginx.org/en/docs/http/ngx_http_core_module.html#client_max_body_size)

### S14 — Nginx FastCGI 请求缓冲

- 来源：Nginx 官方文档。
- 用途：`fastcgi_request_buffering` 的处理顺序与作用。
- 链接：[ngx\_http\_fastcgi\_module / fastcgi\_request\_buffering](https://nginx.org/en/docs/http/ngx_http_fastcgi_module.html#fastcgi_request_buffering)

### S15 — Nginx 条件与返回指令

- 来源：Nginx 官方文档。
- 用途：`if` 与 `return` 的配置语法。
- 链接：[ngx\_http\_rewrite\_module](https://nginx.org/en/docs/http/ngx_http_rewrite_module.html#if)

### S16 — Cloudflare PHP2Shell 防护说明

- 来源：Cloudflare 官方博客。
- 用途：其 WAF 部署范围、Free 计划安排及检测机制说明。
- 链接：[Cloudflare deploys protections against php2shell vulnerability](https://blog.cloudflare.com/php-vulnerabilities-waf-protection/)

## 安全维护咨询

需要协助更新 PHP 系统？我们可以评估影响范围、制定更新计划，并验证实际运行的应用。

[咨询安全更新](<https://keendigit.com/zh/contact?service=security-remediation#form>)

[原始页面](<https://keendigit.com/zh/insights/php2shell-remediation-guide>) · [内容索引](<https://keendigit.com/zh/llms.txt>)

KeenDigit LLC · 怀俄明州登记编号: 2026-002106155

[前往怀俄明州州务卿官网核验](<https://wyobiz.wyo.gov/Business/filingSearch.aspx>)

© 2026 KeenDigit LLC · 美国怀俄明州 Sheridan · 保留所有权利。
