PHP2Shell CVE-2026-109618の影響確認と5つの対策手順
- 2026年10月21日(UTC)に出たPHPの緊急セキュリティリリース(通称 PHP2Shell)の要点を、一次情報ベースで整理します
- 「自分のサーバーは影響あるのか」を1コマンドで確かめるチェック用スクリプトを載せています(PHP 8.5.8で動作確認済み)
- 更新できないときの暫定緩和と、その落とし穴(ディストリのバックポート版・CLIとFPMの設定差)も書きます
- 対象読者: PHPのWebサーバー(PHP-FPM / Apache mod_php など)を運用している人、WordPressやLaravelを動かしている人
要点(TL;DR)
- CVE-2026-109618 / CVE-2026-109619 の2件。どちらも CVSS v4.0 9.2(Critical)、認証なしのリモートコード実行につながりうる。
- 修正版は 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35。Webリクエストを処理するPHPはほぼ全部が対象(PHP-FPM、Apache mod_php、php-cgi、FrankenPHP、LiteSpeed)。
- 公式いわく「完全な緩和策はなく、更新だけが安全な選択肢」。設定変更は時間稼ぎにしかならない。
- watchTowrのハニーポットでは 10月21日から悪用が観測されている。後回しにしない。
- 更新したら PHP-FPM / Apache などのワーカーを必ず再起動する(古いプロセスが残ると直っていない)。
動作環境 / 前提
| 項目 | 内容 |
|---|---|
| 情報の時点 | 2026-10-23(php.net のセキュリティアドバイザリ、watchTowr のFAQを参照) |
| チェックスクリプトの動作確認 | macOS(Apple Silicon)+ static-php-cli の PHP 8.5.8 CLI |
| 対象 | PHP 8.2〜8.5 系(8.1 以前は後述) |
この記事は防御側の確認と対策だけを扱います。攻撃の再現手順や悪用コードは載せません。
何が起きたのか:2つの脆弱性
php.net のアドバイザリでは、2件を次のように説明しています(筆者訳・要約)。
| CVE | GHSA | 中身(要約) |
|---|---|---|
| CVE-2026-109618 | GHSA-w8hw-w9fx-j79w | リクエストの準備中にメモリ不足(out-of-memory)で処理が中断されると、内部データが中途半端な状態のまま、後片付け処理が「正しいデータ」として扱ってしまう |
| CVE-2026-109619 | GHSA-6wmf-4rv5-p3rp | リクエストに属する内部状態が、その裏のメモリが解放された後も残り、同じワーカーが処理する次のリクエストがその古い状態を使ってしまう |
ChangeLog上の修正名は「Avoid state corruption via bailout(bailout経由の状態破壊を防ぐ)」です。どちらもアプリのコードではなくPHP本体(Core)の問題なので、フレームワークやCMSの更新では直りません。
特に注意なのが Apache の mod_php です。公式は「mod_phpが最もリスクが高く、アプリのコードが1行も動く前に、認証なしのリクエストで攻撃を起こせることが実証されている」としています。
手順1:版を確認する(チェックスクリプト)
まずは版の確認です。php -v でもよいのですが、修正版との比較と暫定緩和の設定確認を一度にやるスクリプトを用意しました。
<?php
// check_php2shell.php : CVE-2026-109618 / CVE-2026-109619 の修正版かどうかと、暫定緩和の設定を確認する
// 使い方: php check_php2shell.php (Web経由の設定を見たいときは FPM/Apache 側で同じ内容を実行)
$fixed = ['8.2' => '8.2.35', '8.3' => '8.3.36', '8.4' => '8.4.27', '8.5' => '8.5.12'];
$branch = PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION;
echo "PHP " . PHP_VERSION . " / SAPI: " . PHP_SAPI . "\n";
if (!isset($fixed[$branch])) {
echo "[要対応] {$branch} は公式の修正版がない系統です。サポート中の版へ上げるか、配布元のバックポートを確認してください\n";
} elseif (version_compare(PHP_VERSION, $fixed[$branch], '<')) {
echo "[要更新] {$fixed[$branch]} 以上に上げてください\n";
} else {
echo "[OK] 修正版です(更新後にワーカーを再起動したかも確認)\n";
}
// 以下は「更新できるまでの暫定緩和」。安全にはならず、攻撃経路の一部を減らすだけ。
function bytes(string $v): int {
$v = trim($v);
if ($v === '-1') return -1;
$n = (int)$v;
switch (strtolower(substr($v, -1))) {
case 'g': $n *= 1024;
case 'm': $n *= 1024;
case 'k': $n *= 1024;
}
return $n;
}
$checks = [
['session.upload_progress.enabled', ini_get('session.upload_progress.enabled'),
fn($v) => !$v, '0(無効)推奨'],
['memory_limit', ini_get('memory_limit'),
fn($v) => $v === '-1', '-1 推奨(php_admin_value で固定)'],
['post_max_size', ini_get('post_max_size'),
fn($v) => bytes($v) > 0 && bytes($v) <= 32 * 1024 * 1024, '8M(アップロードなし)/ 32M 以下'],
['upload_max_filesize', ini_get('upload_max_filesize'),
fn($v) => bytes($v) < bytes(ini_get('post_max_size')), 'post_max_size より小さく'],
];
if (PHP_VERSION_ID >= 80500) {
$checks[] = ['fatal_error_backtraces', ini_get('fatal_error_backtraces'),
fn($v) => !$v, '0 推奨(8.5のみ)'];
}
echo "\n暫定緩和の設定(更新済みなら参考程度)\n";
foreach ($checks as [$name, $value, $ok, $hint]) {
$mark = $ok((string)$value) ? 'OK ' : '-- ';
printf(" %s %-32s = %-6s %s\n", $mark, $name, var_export($value, true), $hint);
}
PHP 8.5.8(未修正の版)で実際に動かした結果です。
$ php check_php2shell.php
PHP 8.5.8 / SAPI: cli
[要更新] 8.5.12 以上に上げてください
暫定緩和の設定(更新済みなら参考程度)
-- session.upload_progress.enabled = '1' 0(無効)推奨
-- memory_limit = '128M' -1 推奨(php_admin_value で固定)
OK post_max_size = '8M' 8M(アップロードなし)/ 32M 以下
OK upload_max_filesize = '2M' post_max_size より小さく
-- fatal_error_backtraces = '1' 0 推奨(8.5のみ)
手順2:更新する
公式の結論は「更新だけが完全な対策」です。主な入手経路ごとの目安をまとめます。
| 入手経路 | やること |
|---|---|
| 公式ソース / 公式Dockerイメージ | 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 へ上げてビルド・イメージを作り直す |
| Linuxディストリのパッケージ(apt / dnf など) | ディストリ側のセキュリティ更新を当てる(公式いわく、ディストリとLTSベンダーには事前にパッチが渡っている) |
| レンタルサーバー / マネージド環境 | 提供元の告知を確認(Laravel Forge などは告知を出している) |
| PHP 8.1 以前 | 公式の更新は出ない。LTSベンダーのバックポートを使うか、サポート中の版へ移行する |
補足として、PHP 8.2 は 2026年12月31日でサポート終了と告知されています。今回8.2.35を当てても、年内に8.3以降への移行計画は必要です。
手順3:ワーカーを再起動する
ここを忘れると「パッケージは新しいのに、動いているプロセスは古い」状態になります。
# 例(環境に合わせて読み替え。サービス名はディストリ・版で異なる)
sudo systemctl restart php8.4-fpm # PHP-FPM
sudo systemctl restart apache2 # Apache(mod_php)
再起動後は、Web経由で版を確認するのが確実です(理由は「つまずきポイント」で説明します)。
手順4:すぐ更新できないときの暫定緩和
公式アドバイザリは、更新までのつなぎとして次の設定を挙げています。どれも「特定の攻撃経路を減らすだけで、安全にはならない」と明記されています。
| 対象 | 設定 | 注意 |
|---|---|---|
| CVE-2026-109619 | session.upload_progress.enabled=0 |
php.ini、FPMプール、サーバー設定などリクエスト解析より前に効く場所で設定し、全ワーカーを再起動。ini_set() や FPMの .user.ini には頼らない |
| CVE-2026-109619(8.5のみ) | fatal_error_backtraces=0 |
確認済みのバックトレース経由の経路だけを塞ぐ |
| CVE-2026-109618 | memory_limit=-1 |
アプリが上書きできない形(FPMプールやApache設定の php_admin_value)で設定 |
| CVE-2026-109618 |
post_max_size = 8M(アップロードなし)/ 32M(アップロードあり) |
upload_max_filesize は post_max_size より小さく |
PHP-FPMのプール設定に書く場合の例です。
; /etc/php/8.4/fpm/pool.d/www.conf などに追記(パスは環境で異なる)
php_admin_value[session.upload_progress.enabled] = 0
php_admin_value[memory_limit] = -1
php_admin_value[post_max_size] = 8M
php_admin_value[upload_max_filesize] = 2M
あわせて、更新できないPHPサーバーはインターネットからのアクセスを絞る(WAF・IP制限・一時停止)ことも勧められています。
手順5:再確認する
更新・再起動の後に、もう一度チェックスクリプトをWeb経由で実行し、[OK] と表示されることを確認します。確認が終わったら、スクリプトは公開ディレクトリから必ず消してください(PHPの版や設定は攻撃者にとっても有益な情報です)。
つまずきポイント
ディストリのパッケージだと「要更新」と出るのに直っている
Ubuntu や RHEL 系などのディストリは、**元の版番号を上げずに修正だけを取り込む(バックポート)**ことがよくあります。この場合、PHP_VERSION は古い番号のままなので、チェックスクリプトは [要更新] と出します。
ディストリのパッケージを使っている場合は、版番号ではなくディストリのセキュリティ情報(USN、RHSAなど)でCVEが修正済みかを確認してください。このスクリプトの版判定は、公式の版番号を使っている環境向けです。
CLIで確認した設定と、Webで効いている設定が違う
php check_php2shell.php はCLIの設定(php.ini のCLI用)を読みます。PHP-FPMやApacheは別の設定ファイルを読んでいることが多く、CLIで OK でもWeb側は未設定、ということが普通に起こります。暫定緩和を入れたかどうかは、Web経由で実行して確認してください。
memory_limit=-1 はトレードオフ
memory_limit=-1 はメモリ上限をなくす設定です。今回の脆弱性の経路は減らせますが、重い処理や暴走したスクリプトがサーバーのメモリを食い尽くすリスクが上がります。あくまで更新までの短期間だけにして、更新後は元の値に戻すのが無難です(この点は公式アドバイザリには書かれておらず、筆者の運用上の判断です)。
未検証の点
- 実際の脆弱性の再現・悪用可否は検証していません(防御側の確認だけ)。
- チェックスクリプトの動作確認は PHP 8.5.8 CLI だけです。8.2〜8.4 での実行と、FPM/Apache 上での表示は未確認です。
まとめ
- PHP2Shell(CVE-2026-109618 / CVE-2026-109619)は CVSS 9.2・認証なしRCE・悪用観測あり。Webで動くPHPはほぼ全部が対象。
- 対策の本命は 8.5.12 / 8.4.27 / 8.3.36 / 8.2.35 への更新+ワーカー再起動。設定による緩和は時間稼ぎ。
- ディストリのパッケージは版番号ではなくCVEの修正状況で判断する。
- 設定の確認はCLIではなくWeb経由で。確認用スクリプトは使ったら消す。
- PHP 8.2 は年内でサポート終了、8.1 以前は公式の修正なし。移行計画も同時に立てる。
次に読む / 参考
- PHP公式 セキュリティアドバイザリ・リリース告知: https://www.php.net/
- PHP 8 ChangeLog: https://www.php.net/ChangeLog-8.php
- watchTowr PHP2Shell FAQ: https://watchtowr.com/intelligence/php-cve-2026-109618-faq/
- Laravel Forge の告知: https://laravel.com/forge/docs/knowledge-base/php-security-release-october-2026
同じ週には Python でも証明書まわりの緊急リリース(3.15.1 など)が出ています。言語ランタイムの更新を定期的に回す仕組みづくりについても、別の記事で書く予定です。役に立ったらストックしてもらえると続編の励みになります。