Integral Security Reportインテグラル セキュリティ レポート

PHP2Shellとは:10月21日に多数のWebサービスが止まった、PHPの脆弱性の仕組み

2026年10月21日、PHPの深刻な脆弱性2件を受けて、多数のWebサービスが緊急メンテナンスに入りました。どんな脆弱性で、悪用されると何が起きうるのか。公式アドバイザリをもとに、比喩に頼らず解説します。

公開 2026.10.22 20:10 執筆 Integral 読了 約20分

本記事について

2026年10月21日、多数のWebサイト・サービスが緊急メンテナンスを実施しました。本記事を執筆している10月22日19時の時点でも、メンテナンスが続いているサービスが多数あります。

本記事では、PHPの開発チームが公開した脆弱性とその影響について、できるだけ平易に、しかし正確に(比喩はできるだけ使わず)記載しています。なお、日時はすべて日本時間です。

何が起きたのか(経緯)

脆弱性は、2026年8月にセキュリティ企業のCalifやWiz Researchなど、外部の研究者がPHPに報告したものです。本来は修正版の完成を待ち、修正版と詳細を同時に公開する予定でした。ところが公開前に情報が漏れ、予定が前倒しされました。

  • 当初の予定:10月23日 0:00 に公開
  • 10月17日:Amazonが、自社の観測網で悪用の試行をすでに観測していた(後に公表)
  • 10月21日 0:20ごろ:海外の技術者向け掲示板Hacker Newsなどに「PHPに深刻な脆弱性があり、近く開示される」とする投稿が出て拡散(後に削除)。調査会社watchTowrが「情報は本物」と表明
  • 10月21日 朝まで:PHPが「深刻度が高い(CVSS 9.2)脆弱性2件の修正版を21日 23:00 に公開する」と予告。中身は伏せたまま
  • 10月21日 昼:Cloudflareが、攻撃とみられる通信を遮断するルールを自社のWAFに追加
  • 10月21日 夕方〜:IPAなどが注意を呼びかけ、国内では予防のためにサービスを止める組織が出始める。Cloudflareは早い段階の攻撃を観測したと発表
  • 10月21日 23:00ごろ:修正版が公開され、CVE番号・影響バージョン・技術的な内容が初めて公表(PHPと、報告した研究者らによる共同の開示)

PHPは前倒しの理由を明言していませんが、リークを受けたものと報じられています。リークから公開までの1日足らずの間に出回った「CVSS 10.0」「PHP 5以降すべてが対象」などの情報は、公式の内容と食い違っています(CVSSは脆弱性の深刻度を10点満点で表す指標。公式は9.2としています)。この記事の技術的な内容は、公開後の公式アドバイザリだけをもとにしています。

補足

「PHP2Shell」は、この2件を指して調査会社や報道で使われた通称です。PHPの公式が付けた名前ではありません。

前提:リクエストと、PHPがそれを処理する流れ

リクエストとは何か

この脆弱性を理解するには、まず「リクエスト」を知っておく必要があります。

Webサイトを使うとき、ブラウザはそのたびにサーバーへ1回分の依頼を送っています。これがリクエストです。ページを開く、ログインする、フォームを送信する、投稿を削除する。こうした操作の一つひとつが、1通のリクエストになります。サーバーはそれを処理して、結果をレスポンスとして返します。10人が同時にページを開けば、サーバーには10通のリクエストが届きます。

1通のリクエストは、おおまかに次の部品でできています。

部品 何を表すか 例
メソッド 何をしたいか GET(データを取得したい)、POST(データを送りたい)、DELETE(削除したい)
URL どこに対しての依頼か https://example.com/login
ヘッダー 付随する情報。何行も並ぶ ブラウザの種類、言語、本文の形式やサイズ
本文(ボディ) 送るデータそのもの。主に POST で使われる フォームの入力内容、アップロードするファイル

リクエストは誰でも簡単に、自由に送れる

ふだん私たちが送っているリクエストは、サイトの画面(ボタンやフォーム)が作ったものです。そのため、中身はサイトの作り手が想定したとおりになっています。しかし、リクエスト自体は決まった形式のメッセージにすぎず、ブラウザでなくても作れます。たとえば次のような方法があります。

  • コマンド1行:多くのパソコンに最初から入っている curl というコマンドを使えば、任意のURLに、任意のメソッド・ヘッダー・本文でリクエストを送れます(例:curl -X POST -d "name=test" https://example.com/any/page)
  • 自分で書いたHTML:自分のパソコンに作ったHTMLファイルのフォームから、他人のサイトの任意のURLに向けてデータを送信できます
  • 開発用のツール:Webの開発者がAPIの動作確認に使うツールでは、画面上でヘッダーや本文を自由に書き換えて送信できます
  • 数行のプログラム:同じリクエストを、自動で何千回も送ることもできます

つまり攻撃者は、サイトの画面にフォームがあるかどうかに関係なく、どのURLにでも、想定外の中身のリクエストを、何度でも送れます。サーバーの側では、届いたリクエストが正規の画面から来たのか、手作りされたものなのかを確実に見分けることはできません。届いたものは一つひとつ処理するしかないのです。

PHPがリクエストを処理する流れ

PHPを実行するプロセス(ワーカー)は、届いたリクエストを1件ずつ次の①〜③の流れで処理します。

段階 PHPがすること アプリのコード(各サイトの開発者が書く部分)
① 開始処理 リクエスト用のメモリを用意する。POSTの本文を解析し、$_POST や $_FILES を作る まだ動いていない
② 実行 アプリのコード(index.php など)を動かす 動く
③ 終了処理 後片付けをして、リクエスト用のメモリをまとめて捨てる 終わっている

③が終わると、同じワーカーが次のリクエストの①に戻ります。ワーカーは起動し直さず、何百・何千ものリクエストを順に処理し続けます。

PHPの大きな特徴は、③でそのリクエストのために使ったメモリを全部捨てることです。次のリクエストは前のリクエストの影響を受けず、まっさらな状態から始まります。プログラマがメモリの後始末を細かく気にしなくてよいのは、この仕組みのおかげです。

今回の2件は、この流れの中で次のことが起きるバグです。

  • CVE-2026-109618:①の途中で処理が打ち切られ、中途半端な状態のメモリを③が正しいものとして扱ってしまう
  • CVE-2026-109619:③で捨てたはずのメモリへの参照がワーカー全体の情報に残り、次のリクエストがそれを使ってしまう
補足

プロセスは、コンピューターの上で動いている1つのプログラムの実行単位です。パソコンでブラウザやメールソフトを開くと、それぞれがプロセスとして動いています(Windowsのタスクマネージャーや、Macのアクティビティモニタに並んでいるもの)。Webサーバーの上では、PHPを実行するプロセスが何個か起動していて、これをワーカーと呼びます。

メモリは、プログラムが動いている間、作業中のデータを一時的に置いておく場所です。送られてきたフォームの内容も、処理の途中ではメモリに置かれます。メモリの量には限りがあるので、PHPは1つのリクエストが使えるメモリに上限(memory_limit)を設けています。

PHPのワーカーは、メモリを大きく2種類に分けて使っています。

  • リクエスト用のメモリ:そのリクエストの処理のためだけに使い、処理が終わったらまとめて捨てる
  • ワーカー全体で持ち続ける情報:設定やエラーの状態など、リクエストをまたいで残るもの

この区別は、あとで説明する2つ目の穴に関わってきます。

もう少し詳しく:PHPの内部では

①はPHP内部のリクエスト開始処理(php_request_startup())、③は終了処理(php_request_shutdown())にあたり、拡張モジュールから見ると RINIT / RSHUTDOWN の段階です。①でのPOST本文の解析は、アプリが $_POST を読むかどうかに関係なく、PHP本体が自動で行います。

1つ目の穴:初期化の途中で打ち切られる(CVE-2026-109618)

PHPには、1リクエストで使えるメモリの上限(memory_limit)があります。上限を超えると、PHPはその場で処理を打ち切ります。

問題は、この打ち切り方でした。PHPはメモリ不足などの致命的なエラーが起きると、途中の処理を飛ばして、あらかじめ決めておいた地点まで一気に戻ります(C言語の longjmp() という仕組み)。飛ばされた処理の続きは実行されません。

これが①の途中、リクエスト用のメモリを用意し終える前に起きると、用意しかけのメモリが中途半端な状態のまま残ります。そのあと③の終了処理が、そのメモリを正しく用意されたものとして読み、その内容に従って動いてしまう。公式アドバイザリは、これがリモートからのコード実行につながると説明しています。

修正版では、メモリ不足のときに longjmp() で処理を飛ばすこと自体をやめています。

攻撃者から見ると、何が「都合がいい」のか

ここで「メモリが足りなくなるのは、たまたまでは」という疑問が出ます。実際には、攻撃者はこの状況を狙って作れる、というのがこの脆弱性が深刻とされる理由です。細かい手口には踏み込みませんが、イメージとして次の3点を押さえると、なぜ深刻なのかが分かります。

  • 処理の重さは、送る側が左右できる:さきほど見たとおり、リクエストの中身は攻撃者が組み立てます。サーバーにとって処理が軽いリクエストもあれば、重いリクエストもあります。攻撃者は、PHPが①の開始処理の段階で大きなメモリを使わざるを得ないような、わざと「重い」リクエストを送れます。つまり、メモリ不足という状態を、好きなタイミングで作り出せます。
  • アプリのコードより前に起きる:この処理は、アプリの index.php が動き出す前の、PHP本体による前処理の段階で起きます。アプリ側でどれだけ入力を検証しても、そこに到達する前の話なので防げません。ログイン画面しかないサイトでも、ログイン前に処理されます。
  • 何度でも試せる:1回のリクエストは一瞬で、送るコストもほとんどかかりません。攻撃者は同じことを何千回でも繰り返せます。後で触れるとおり、この攻撃は一度で成功するとは限りませんが、「成功しにくい」ことは「安全」を意味しません。回数で確率を上げられるからです。

まとめると、攻撃者は「アプリのコードでは防げない段階で」「好きなときに攻撃を仕掛けられ」「失敗しても何度でもやり直せる」。ネットワーク越しに認証なしで攻撃でき、成功したときの影響も大きいため、この脆弱性はCVSSで9.2(Critical)と評価されています(CVSSの読み方は補足を参照)。

2つ目の穴:前のリクエストの「残骸」を使ってしまう(CVE-2026-109619)

①〜③の流れで、ワーカーは使い回されます。リクエスト用のメモリは③で捨てますが、ワーカー全体で持ち続ける情報(エラーの状態、セッション関連の状態など)は残ります。

通常は、③でこれらの情報もリセットされます。ところが、致命的なエラーなどでリセットが省略されると、ワーカー全体の情報が、捨てたはずのリクエスト用メモリを指したままになります。次のリクエストが来たとき、本来の初期化より前にその情報を読むと、もう別の用途に使われているかもしれないメモリを参照してしまいます。これが use-after-free(解放済みメモリの使用)です。

公式アドバイザリによると、この「初期化前に古い情報を読んでしまう処理」の一つが、ファイルアップロードの進捗をセッションに記録する機能(session.upload_progress)です。この機能はデフォルトで有効で、アプリが session_start() を呼んでいなくても動きます。

影響として、Webワーカーの権限でのコード実行に加えて、メモリの内容の漏えいも確認されています。

なぜ対象は「PHPで動いているもの全部」なのか

「フォームやファイルアップロードがあるサービスだけが危ないのでは」と思うかもしれません。そうではありません。

今回の2件は、どちらもアプリのコードが動く前に、PHPが自動で行う処理の中で起きます。POSTの本文の解析も、アップロード進捗の記録も、アプリがフォームを持っているかどうかに関係なく、リクエストが届けばPHPが自動で行います。

そして前提の節で見たとおり、リクエストは誰でも、どのURLにでも、自由な中身で送れます。サイトにフォームがなくても、攻撃者はPHPが処理するURLに直接リクエストを送れば済みます。

つまり、PHPが処理するURLが外から1つでも届く状態なら、そこが入口になりえます。そのため、公式の影響範囲は「Webリクエストを処理するすべてのPHP」となっています。

攻撃が成功したら何が起きうるか

起きうる被害

サーバーの上では、プログラムもパソコンと同じように、あるユーザーアカウントとして動いています。PHPの場合、多くは www-data や apache といった、サイトを動かすための専用アカウントです。攻撃が成功すると、攻撃者の送り込んだプログラムがこのアカウントとして動きます。つまり、そのサイトのプログラムにできることは、攻撃者にもできるようになります。

会員制のECサイトなら、会社や利用者から見て次のような被害が考えられます。

起きうること 会社・利用者から見ると
会員情報の漏えい 氏名、住所、メールアドレス、注文履歴などが外部に持ち出される。利用者への連絡や、個人情報保護委員会への報告が必要になる場合がある
外部サービスのパスワードやキーの流出 サイトがつないでいるデータベース、メール配信、決済、クラウドなどの認証情報が漏れる。PHPを更新しても、これらを変更しない限り悪用されうる
サイトの改ざん ページの内容が書き換えられ、訪れた利用者が被害を受けるおそれがある。気づかないまま運営を続けてしまうこともある
修正後も残る不正なファイル 一度置かれると、PHPを更新しても消えない。更新だけでは「元どおり」にならない
社内やクラウドの別のシステムへの被害 そのサーバーからつながる別のシステムにまで、被害が広がる可能性がある
サーバーの悪用 自社の名前で迷惑メールが送られて信用を失う、クラウドの利用料が想定外に増える、他社への攻撃の発信元にされる

2つ目の穴では、メモリの内容の漏えいも確認されています。同じワーカーで処理された、他の利用者のリクエストの内容が読まれる可能性があります。

それだけではできないこと

  • サーバーの管理者(root)権限の取得:Webサイト用のアカウントの権限を超えるには、別の脆弱性や設定の不備が必要です。
  • 権限が分かれた他のユーザーのファイル:パーミッションが適切なら読めません。

ただし、次のような構成では被害の範囲が広がります。

  • コンテナ内でPHPを root で動かしている
  • 1台のサーバーで複数のサイトを同じユーザーで動かしている(共用サーバーなど)
  • ドキュメントルート全体にWebサイト用のアカウントが書き込める

まとめ

今回の2件は、PHPが「リクエストごとにメモリを用意し、終わったら捨てる」流れの中で、エラーが起きたときの後始末が正しく行われないことから生じた脆弱性です。

  • 問題が起きる場所:アプリのコードが動く前の、PHP本体の処理の中です。そのため、少なくとも一部の動かし方では、アプリの作りやフォームの有無に関係なく攻撃が成立し、対象はPHPで動くWebサービス全体に及びます(動かし方による差は補足を参照)。
  • 成功した場合の被害:そのサイトのプログラムにできることが、攻撃者にもできるようになります。漏れた認証情報や置かれた不正なファイルのように、PHPを更新しただけでは元に戻らない被害もあります。
  • 確からしさ:攻撃は一度で確実に成功するものではなく、PHPの動かし方によって実証の程度にも差があります。「何もかも乗っ取られる」わけでも、「成功しにくいから安全」なわけでもありません。

10月21日に多くのサービスが予防的に止まった背景には、修正版の公開前で、中身が分からないまま「深刻」という予告だけが先に出ていたことがあります。公開された内容を見ると、深刻であることに変わりはありませんが、原因と影響範囲は明らかになっています。

メンテナンスが続いているサービスについて

修正版の公開から20時間近くたった執筆時点でも、再開していないサービスがあります。理由を公表していない組織が多く、外からは分かりませんが、次のような事情が考えられます。

  • 更新後の動作確認:修正版を適用したあと、サービスが正しく動くかを確かめるには時間がかかる
  • 侵入されていないかの調査:公開前の10月17日から攻撃の試行が観測されていたため、停止する前に攻撃を受けていないかを、ログなどで確かめてから再開しようとしている
  • 提供元の対応待ち:レンタルサーバー、CMS、パッケージ製品などでは、利用者が自分でPHPを更新できない
  • 修正版がないバージョンを使っている:PHP 8.1以前には、PHPの公式の修正版は出ていません。サポート対象のバージョンに上げるにはアプリの改修が必要になることが多く、すぐには再開できない

最後の点は、サポートが終わったバージョンを使い続けることの代償が、こうした緊急時に表に出ることを示しています。

具体的な対応の手順は、PHP公式のアドバイザリを確認してください。本記事は、新しい情報が出しだい更新します。


補足資料

ここからは、さらに詳しく知りたい方向けの補足です。

影響バージョンと修正版

ブランチ 修正版 備考
8.5 8.5.12
8.4 8.4.27
8.3 8.3.36
8.2 8.2.35 2026年12月31日でサポート終了
8.1以前 なし PHPプロジェクトとしての通常の修正版は出ない。OSのディストリビューションや長期サポートのベンダーが独自に修正を提供している場合がある【要確認:公式告知にある 7.4 / 8.0 / 8.1 向けの「最小限のバックポート」の扱い】

影響を受けるのは、各ブランチの修正版より前のすべてのバージョンです。8.1以前について、PHPプロジェクトは影響範囲を明記していません(AWSは5.4以降が影響を受けるとしています)。

動かし方の面では、Webリクエストを処理するすべてのPHPが対象です(PHP-FPM、Apache mod_php、php-cgi、FrankenPHP、LiteSpeed)。

分かっていないこと

  • PHP-FPMでの完全な攻撃:ソース解析上は影響があるとされていますが、アドバイザリの時点では実証されていません(下の「動かし方(SAPI)による違い」を参照)。
  • 攻撃の安定性:アドバイザリは、攻撃が成功するかどうかはPHPのビルド、拡張モジュール、フロントのWebサーバーの動作、メモリの割り当て方に左右されるとしています。
  • 実際の悪用:悪用の試行は観測されています。Amazonは公開の4日前の10月17日から自社の観測網で試行を観測し、複数の攻撃者によるものとみています(Tenableの記事による)。watchTowrやCloudflareも、試行や早い段階の攻撃を確認したとしています。一方、実際に侵入されて被害が出たという事例が公表されているかどうかは、本記事の執筆時点では確認できていません。【要確認:公開時点の状況に更新】

CVSS 9.2 をどう読むか

ベクトルは AV:N/AC:H/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H です。

  • ネットワーク越しに、認証なし・利用者の操作なしで攻撃できる(AV:N / PR:N / UI:N)
  • 一方で、攻撃の複雑さは「高」、攻撃が成立するための前提条件は「あり」(AC:H / AT:P)

満点の10ではないのは、この「複雑さ」と「前提条件」のためです。ただしアドバイザリは、ワーカーが多い環境でも、狙ったワーカーに次のリクエストが届くまでの試行回数が増えるだけで、脆弱性そのものが成立しなくなるわけではない、としています。一度で成功しにくいことは、安全であることを意味しません。

動かし方(SAPI)による違い

PHPをWebサーバーでどう動かしているか(SAPI)によって、1つ目の穴の確からしさに差があります。アドバイザリの記載を整理すると次のとおりです。

方式 1つ目の穴(109618) 状態
Apache mod_php 開始処理に失敗しても終了処理を必ず実行するため、アプリに関係なく成立する 完全なRCEを検証済み
PHP-FPM 開始処理に失敗すると子プロセスごと終了するため、その経路では古い状態が使われない。アプリの実行中にメモリ不足が起きる経路では、同じ問題が起こりうる ソース解析上は影響あり。完全な攻撃は未実証
FrankenPHP mod_php と同じく終了処理を必ず実行する 影響を受けやすいと推定(未検証)
LiteSpeed(LSAPI) FPMに近い作り アプリ次第で成立しうる(未検証)

2つ目の穴(109619)は、ワーカーを使い回すすべての方式に関わります。

つまり、方式による差は「どこまで実証されているか」の差であり、PHP-FPMなら影響を受けない、という意味ではありません。

公式アドバイザリが挙げる緩和策と、その限界

修正版に更新できない場合の緩和策として、公式アドバイザリは次のものを挙げています。いずれも完全な対策ではないと明記されており、修正版への更新が唯一の確実な対策とされています。

緩和策の狙い 対象 限界
アップロード進捗の記録機能を止める 109619 設定ファイルの書き方によっては効かない。問題の処理がアプリのコードより前に動くため
1リクエストで使えるメモリの上限をなくす 109618 サーバー全体のメモリを使い切られるリスクと引き換えで、問題の経路が残る場合もある
リクエストの大きさを手前のWebサーバーで制限する 109618 ファイルアップロードや一部のフォームが動かなくなる
ワーカーをリクエストごとに作り直す 109619 性能への影響が大きい

いずれも、問題が起きる条件を減らすだけで、ふさぎ切れるわけではありません。具体的な設定方法と、やってはいけない設定(かえって危険になるもの)は、公式アドバイザリに書かれています。

また、エラーログに痕跡がないことは、攻撃を受けていないことの証明にはならない、とも注意されています。

クラウド・CDN事業者による防御

Webサイトの手前で通信を中継・検査する事業者が、PHPの修正版の公開前後に、攻撃とみられるリクエストを遮断する仕組み(WAF)を独自に用意しました。

事業者 内容 時期
Cloudflare WAFに専用のルールを追加。無料プランを含むすべてのプランで、初期設定のまま有効 10月21日 12:00ごろから(修正版の公開前)
AWS AWS WAFのPHP向けマネージドルールを更新。あわせて、Elastic Beanstalk、Amazon Linux、Lightsailなど、自社サービスに含まれるPHPを順次更新 10月22日 6:30 に公表

ただし、両社ともWAFは一時しのぎで、PHPの更新の代わりにはならないと明言しています。

  • そのサービスを通る通信にしか効かない:CDNを経由せずにサーバーへ直接届く経路があれば、その通信は検査されない
  • 利用者の設定によって効き方が変わる:AWSでは、使っているルールのバージョンの確認や、利用者が手動で追加するルールが必要な場合がある
  • すり抜けの試み:Cloudflareは、攻撃者がリクエストの形を変えてルールを回避しようとする動きを観測している

10月21日に止まらずに済んだサイトの中には、こうした事業者の防御の内側にあったものも含まれると考えられます。

分類(CWE)と修正の方針

アドバイザリの分類(CWE)は、2件とも次のとおりです。

  • CWE-416:解放済みメモリの使用(use-after-free)
  • CWE-755:例外的な状況の不適切な処理
  • CWE-824:未初期化ポインタへのアクセス
  • CWE-1325、CWE-1419

「メモリ不足」そのものが問題なのではなく、エラー処理の経路で、状態の整合性が保たれていなかったことが本質です。1つ目の穴の修正では、メモリ不足のときに longjmp() で処理を飛ばすこと自体をやめています。

参考

自社は大丈夫か、一度確認しませんか。

事例をもとにした社内勉強会や、委託先・クラウド構成のセキュリティ確認をご相談いただけます。

お問い合わせ →