2026年10月21日、PHPプロジェクトは、未認証の攻撃者がウェブサーバー上でコードを実行できる2件の重大なバグに対する緊急リリースを公開しました。この2件はCVE-2026-109618とCVE-2026-109619として追跡され、PHP2Shellという名前で呼ばれています。どちらもCVSS v4.0で9.2を記録し、どちらもHTTPを提供するあらゆるPHP環境に影響し、Amazonのハニーポットはパッチが存在する4日前の10月17日に悪用の試行を記録していました。
サーバーサイド言語が判明しているウェブサイトの69.3%でPHPが稼働しています(W3Techs)。PHPのAPI、LaravelやSymfonyのバックエンド、WordPressサイト、あるいはPHPを組み込んだコンテナイメージを運用しているなら、これはあなたにも関係する話です。
このガイドでは、これらのバグが何をするのか、どのバージョンが影響を受けるのか、サーバーをどう確認するのか、緩和策が実際にどこまで効果があるのか、そしてパッチ適用後もAPIが正常に動作することをどう証明するのかを扱います。
TL;DR
| 質問 | 回答 |
|---|---|
| 何なのか? | PHPのリクエスト処理における2件のメモリ安全性バグで、連鎖させるとリモートコード実行に至る |
| CVE | CVE-2026-109618(メモリ不足時の破壊)、CVE-2026-109619(古い状態の残存、use-after-free) |
| 深刻度 | CVSS v4.0で各9.2(初期に漏れたIDでは10.0とされていたが、PHPはそれが誤りだと述べている) |
| 認証は必要? | 不要 |
| 影響範囲 | HTTPを提供するすべてのSAPI: PHP-FPM、Apache mod_php、php-cgi、FrankenPHP、LiteSpeed |
| 修正済みバージョン | 8.5.12、8.4.27、8.3.36、8.2.35 |
| PHP 8.1 以前 | 公式リリースなし。7.4、8.0、8.1向けに最小限のソースバックポート。5.4〜7.3は何も提供されない |
| 悪用されている? | 10月17日以降に試行を確認(Amazon)。Cloudflareは初期の攻撃を報告 |
| 修正方法 | アップグレードし、すべてのワーカーを再起動する。回避策はリスクを下げるが、問題を塞ぎきることはできない |
PHP2Shell の正体
両方のバグに共通する根本原因は1つです。リクエストが異常終了したときにPHPがメモリを壊れた状態のまま残すことがあり、その後のクリーンアップ処理がその壊れた状態を信頼してしまうのです。
CVE-2026-109618: リクエスト起動時のメモリ不足による破壊。 PHPがフォームデータを$_POSTにパースする際、Zendエンジンのハッシュテーブルコードは、新しいキーを割り当てる前にエントリカウンタをインクリメントします。Datadogの分析では、zend_hash.c内の_zend_hash_str_add_or_update_i()に起因すると特定されています。その割り当てがmemory_limitを超えると、PHPは致命的エラーを発生させて挿入を中止します。しかしカウンタには、決してセットアップされなかったバケットが含まれたままです。シャットダウン時にPHPは$_POSTを破棄し、カウントされたすべてのバケットを実在するものとして扱います。
APIチームが気にすべき点は、これがPHPがまだリクエストボディをパースしている最中、つまりあなたのコードが1行も実行される前に起きるということです。認証ミドルウェア、レートリミッタ、入力検証はこのリクエストを一切見ません。Datadogはファイルアップロードではなく、単純なapplication/x-www-form-urlencodedボディで再現しました。
CVE-2026-109619: リクエストをまたいで残る古いグローバル状態。 PHPはワーカー全体で共有される状態をいくつか保持しており、それがリクエストごとのメモリを指しています。その状態のリセットを致命的エラーが中断した場合、あるいは後発のシャットダウン診断がクリーンアップ後にそれを再生成した場合、同じワーカーで処理される次のリクエストが解放済みメモリを読み取ります。PHPは2つの経路を確認しています。
- 致命的エラーのバックトレース(PHP 8.5のみ): 後続のリクエストで設定診断が残留したバックトレース状態を読み取ります。mod_phpでのコマンド実行が確認されています。
- アップロード処理: 致命的エラーの後、セッションアップロード進捗が後続のアップロードのために状態を残すことがあります。アップロード進捗はデフォルトで有効で、アプリが
session_start()を一度も呼ばなくても動作します。mod_phpとPHP-FPMでのコマンド実行が確認されています。
PHPセキュリティチームはmod_phpを最もリスクが高いと評価しています。実証された攻撃は、アプリケーションコードが読み込まれる前の未認証リクエスト1回で発火するためです。
誰が見つけたのか(そしてなぜAIが注目されるのか)
これらのバグはPHP 5.4.0で導入されたコードに由来するため、10年以上コードベースに存在していました。PHP FoundationはCalif、OpenAI、Wizをクレジットしています。
DecryptとThe Next Webの報道によれば、Califの研究者Ryan Austinが8月、OpenAIのGPT-5.6 Solの支援を受けたソース監査中にこれらの欠陥を発見しました。OpenAIは、自社チームが9月に「Patch the Planet」活動の一環としてより完全なバージョンを発見したと述べています。WizはGemini 4 Argon、Wiz Atlas、GPT-5.6 Solを用いて両方のバグを独自に発見したと述べています。功績の帰属は争われていますが、結論は変わりません。AI支援の監査は今や中核インフラに存在する10年前のメモリバグを発見しており、攻撃者も同じモデルを手にしています。
タイムライン: なぜパッチが1日前倒しになったのか
| 日付(2026年) | 出来事 |
|---|---|
| 8月〜9月 | Calif、OpenAI、Wizが非公開でバグを報告 |
| 10月17日 | AmazonのMadPotハニーポットが悪用の試行を記録 |
| 10月20日 | CVE IDがHacker NewsやSNSで漏洩。PHPが事前通知を掲載。CERT-EUが勧告2026-018を発行 |
| 10月21日 14:00 UTC | PHPが修正をリリース(漏洩を受けて10月22日から前倒し)。CISAが警告を公開。Cloudflareが初期の攻撃を報告 |
| 10月22日 | 両CVEともまだCISAのKEVカタログには登録されていない |
Amazonは開示日ではなく10月17日以降を調査対象とするよう勧めています(TenableのFAQが全タイムラインを追跡しています)。リリース時点でPHPチームは、漏洩後に出現した実証コード(PoC)を検査し、動作するエクスプロイトは見つからなかったと述べました。その後watchTowrは両方のバグを再現したと発表しています。見つけた「PoC」リポジトリは疑ってかかってください。検証されていないエクスプロイトコードはマルウェアを同梱していることがよくあります。
影響を受けていますか?5分で確認する方法
1. ノートPCではなく、トラフィックを処理しているランタイムを確認する。 CLIバイナリはFPMやApacheモジュールとバージョンや設定が異なる場合があります。
# On the server or inside the running container
php -v
php-fpm -v
# Apache mod_php: check the loaded module version
apachectl -M 2>/dev/null | grep -i php
または、保護されたルートから一度だけ公開し、確認後に削除します。
<?php echo PHP_VERSION, ' ', PHP_SAPI;
2. 修正済みバージョンと比較する。
| ブランチ | 脆弱なバージョン | 修正版 |
|---|---|---|
| 8.5 | < 8.5.12 | 8.5.12 |
| 8.4 | < 8.4.27 | 8.4.27 |
| 8.3 | < 8.3.36 | 8.3.36 |
| 8.2 | < 8.2.35 | 8.2.35 |
| 7.4〜8.1 | すべて | ソースバックポートのみ、またはベンダーのLTS |
| 5.4〜7.3 | すべて | 修正なし |
Debian、Ubuntu、Red Hatなどのディストリビューションは、上流のバージョン文字列を変えずに修正をバックポートすることがよくあります。php -vが古い番号を示していても、慌てる前にディストリビューションの勧告を確認してください。
3. 隠れたPHPを洗い出す。 数か月前にFROM php:8.3-fpmでビルドしたコンテナ、ベンダー製アプライアンス、自前でコンパイルしたビルド、忘れられたサブドメイン上の管理パネル。Ciscoはすでに自社製品ラインを確認するための勧告を出しています。Shodanはインターネットに公開されたPHPホストを190万件以上と数えており、ある研究者は130万件以上が脆弱である可能性があると推定しています。
パッチ適用の方法
# Debian / Ubuntu (Ondřej Surý PPA or distro packages)
sudo apt update && sudo apt install --only-upgrade 'php8.*'
# RHEL / Rocky / Alma with Remi
sudo dnf upgrade 'php*'
# Official Docker image: rebuild on a fixed tag
docker pull php:8.4.27-fpm
docker build --pull -t my-api:patched .
その後、ほとんどのチームが忘れるステップを実行します。すべてのPHPワーカーを再起動または置き換えてください。 稼働中のFPMプールはメモリ内に古いバイナリを保持したままです。php-fpmを再起動し、Apacheをリロードし、すべてのコンテナをロールしてください。CVE-2026-109619はワーカー内でリクエストをまたいで存在する状態に関するものなので、再起動していないワーカーは依然として脆弱です。
8.1以前の場合、選択肢は3つあります。ベンダーのLTSパッケージ、PHPのソースバックポートからのビルド、または8.2以降へのアップグレードです。CISAはパッチを適用できないPHPシステムをオフラインにすることを推奨しており、英国のNCSCとFBIも同様の見解を示しています。
今日中にパッチを適用できない場合の緩和策
これらは時間を稼ぐものです。PHPコアチームのGina Peter BanyardはBleepingComputerに対し、どの緩和策も攻撃を完全に阻止するものではないと述べています。
CVE-2026-109618の場合:
- アプリが上書きできない場所(FPMプールやApache設定の
php_admin_value)でmemory_limit=-1を設定する。これで既知の経路はブロックできますが、割り当てが失敗するすべての方法を塞げるわけではありません。 post_max_sizeを小さくする(アップロードなしなら8M、ありなら32M)。- リバースプロキシでリクエストボディのサイズを制限し(nginxの
client_max_body_size)、可用性の境界としてコンテナやcgroupのメモリ制限を設定する。 RLIMIT_ASやRLIMIT_DATAのプロセス制限は使わないでください。それらはエクスプロイトが必要とする割り当て失敗そのものを引き起こす可能性があります。
CVE-2026-109619の場合:
- リクエストのパース前に読み込まれる設定で
session.upload_progress.enabled=0を設定する。 - PHP 8.5では
fatal_error_backtraces=0も設定する。 display_errors=0や@でエラーを隠しても何の意味もありません。古い状態は依然として存在します。
エッジ側: Cloudflareは無料プランを含むすべてのプランで既知の攻撃ベクターに対するWAFルールを展開しました。同社のCTOはそれを一時的なものと述べています。
php.iniを変更した後は、すべてのワーカーを再起動しないと反映されません。
すでに被害を受けている兆候
Datadogの検知ガイダンスは、要約すると4つのシグナルになります。
- ログ行:
PHP Fatal error: Allowed memory size of ... bytes exhaustedとUnknown on line 0の組み合わせ。これはPHPがスクリプトの実行前に、リクエスト起動中に落ちたことを意味します。 - Apacheのクラッシュ:
AH00052: child pid ... exit signal Segmentation fault (11)。エクスプロイトの試行が外れた可能性が高いです。 - 奇妙なフォームPOST: 数メガバイト長のフィールド名、あるいは空の名前のフィールドにバイナリやヌルバイトが含まれている。
- 決定的なもの: ウェブワーカー(
php-fpm、apache2、httpd)がシェルを起動している。
ステータスコードを信じてはいけません。Datadogの改良版エクスプロイトはHTTP 200を返し、ワーカーは稼働し続けました。ログを10月17日まで遡って調査してください。
パッチ適用後もAPIが動作することを証明する: エージェントに実行させる
プレッシャーの中で本番環境のPHPにパッチを当てると、マイナーバージョンの更新で何かが変わったことに後から気づく羽目になります。修正をロールアウトする前と後に、ステージング環境に対してAPIテストスイートを実行し、コーディングエージェントにApidog CLIでそれを実行させましょう。
セットアップ(1回):
npm install -g apidog-cli
apidog auth login --with-token <your-access-token>
apidog test-suite list --project <projectId>
Claude Code、Codex、Cursorへの1つのプロンプト:
PHP2Shell対応のため、ステージング環境のPHPを8.4.27にアップグレードしたところです。Apidog CLIを使ってステージング環境に対して回帰テストスイートを実行し、HTMLとJUnitのレポートを出力し、失敗したエンドポイントを要約してください。その後、nginxのclient_max_body_sizeを超えるボディを持つPOSTが、PHPに到達する前にプロキシ側で413で拒否されることを確認してください。エージェントが実行するもの:
apidog test-suite run <testSuiteId> --project <projectId> -e <stagingEnvId>
apidog run --test-suite <testSuiteId> --project <projectId> -e <stagingEnvId> \
-r cli,html,junit --out-dir ./php2shell-reports --upload-report
-rはレポーターを選択し、--out-dirはレポートの出力先を設定し、--upload-reportはApidogアプリで開けるサマリーをアップロードします。何かが壊れたときだけチームに通知したい場合は--notification-failed-event <targetId>を追加してください。同じapidog runの行をCIに入れておけば、再ビルドされたすべてのPHPイメージが出荷前にテストされます。Claude CodeセットアップガイドとCI/CDパイプラインのウォークスルーで両方の詳細を扱っています。
古いバージョンで一度スイートを実行してベースラインを取り、アップグレード後にもう一度実行して差分を取ります。失敗するはずのないテストが失敗したなら、ユーザーより先に気づくことができます。Apidogをダウンロードして、CLIが実行するスイートの構築と管理を始めましょう。
FAQ
PHP2Shellは悪用されていますか? Amazonは10月17日から試行を確認しており、現在は広く悪用されていると述べています。CloudflareとwatchTowrも攻撃を報告しています。CISAは10月22日時点で悪用を確認していません。
フレームワークを使っていれば安全ですか? いいえ。CVE-2026-109618はPHPがボディをパースしている間に、Laravel、Symfony、WordPressが読み込まれる前に発火します。
共有ホスティングを使っています。どうすればいいですか? ホストのステータスページを確認してください。SiteGroundなどはパッチを適用済みと述べています。あなたのホストが何も発表していないなら、問い合わせてください。
session.upload_progress.enabled=0で十分ですか? いいえ。それは1つのCVEの1つの経路をカバーするだけです。パッチを当ててください。
シークレットをローテーションする必要がありますか? CVE-2026-109619はメモリの開示を可能にします。10月17日以降に侵害の兆候がログに現れている場合は、PHPプロセスが読み取れたデータベースのパスワード、APIキー、セッションシークレットをローテーションしてください。
まとめ
8.5.12、8.4.27、8.3.36、8.2.35のいずれかへアップグレードしてください。すべてのワーカーを再起動してください。忘れていたPHPを見つけてください。ログを10月17日まで遡って確認してください。そしてAPIテストを実行し、修正が次の障害にならないようにしてください。PHPが8.1以前なら、今週こそ移行する時です。
