PHPの脆弱性の「痕跡」は、ログに無いこともある ── 圧縮されたログ・エラーの出る先・回転の直後を落とさずに、件数で数える13項目(2026年10月時点)
PHPの脆弱性の「痕跡」は、ログに無いこともある ── 圧縮されたログ・エラーの出る先・回転の直後を落とさずに、件数で数える13項目(2026年10月時点)
目次
01 何が書いてあるか — 痕跡が見つからなくても、攻撃を受けていないとは断定できない。ログは「数え方」で結果が変わる
引用について
この記事の「" "」で囲んだ英語の文は、公式の文書からの引用で、日本語訳は記事の筆者が付けたものである。日本語の文は、JPCERT/CCのページの文をそのまま引いた。原文は各出典のページで確かめられる。各ページは2026年10月23日に開いて確かめた。
JPCERT/CCの注意喚起が、侵害の調査について書いたこと
JPCERT/CCの注意喚起(JPCERT-AT-2026-0033。2026年10月22日に公開され、同日に「侵害調査」の一部の表現が修正されている)は、PHPの2つの脆弱性(CVE-2026-109618・CVE-2026-109619)について、侵害の有無を調べる観点を3つ挙げる。発見者らが公開している情報として紹介されたもので、1つ目は、異常に長い、または異常な形式の値を持つPHPSESSIDのCookieがHTTPの応答で設定されていないか、2つ目は、不審なmultipart/form-data形式のリクエストと、その前後のメモリ上限のエラーやHTTP 500、3つ目は、PHPのワーカーの異常終了や頻繁な再起動である。3つの観点の紹介と更新の手順は、AI RonのブログのPHPの重大な脆弱性「php2shell」を、初心者から上級者まで1本で ── 自分のサイトは関係あるか、更新の順番、更新の後に確かめること、攻撃を受けたかの調べ方、当サイトとチームの記録にまとめてあり、この記事では繰り返さない。
この記事が扱うのは、その3つをどのログを、どの期間、どう数えるかである。同じ注意喚起は、数えた結果の読み方について、次のように書く。
上述の痕跡が確認されない場合でも、本脆弱性を悪用した攻撃を受けていないと断定することはできません。
理由として、複数の悪用経路があり、単一の検知ルールではすべてを検出できないこと、ログの記録状況によっては痕跡が残らない場合があることを挙げる。
GitHubの勧告が、ログについて書いたこと
CVE-2026-109619の勧告(GHSA-6wmf-4rv5-p3rp)には、「Logging and configuration audit」という節がある。冒頭は、次のとおりである。
"Enable log_errors=On, a functional error_log, and error_reporting=E_ALL in startup configuration during an isolated audit."
(訳: 隔離した監査の間は、起動時の設定で、log_errorsをOn、動作するerror_log、error_reportingをE_ALLにする。)
その後に、ログの限界が書かれている。
"Fixing observed errors reduces exposure, but absence of an error in logs is not proof of safety."
(訳: 見つかったエラーを直せば露出は減るが、ログにエラーが無いことは、安全の証明ではない。)
続けて、理由を3つ挙げる。過去のログは、非推奨などの重大度を除いて集められていたかもしれないこと。E_ALLで監査しても、実際に通った経路と状態しか分からないこと。メッセージをPHPが書く前に失敗することもあること、である。
この記事の範囲
この記事は、「ログに無いから安全」と読まないための記事ではない。ログを数えるときに、数えそこなうものを減らすための記事である。0件という結果に、安心の根拠を持たせるには、どの範囲を数えたかを書く必要がある。
02 なぜ・背景 — ログの0件は、数え方によって「無い」と「見ていない」の両方になる
エラーの出る先は、1か所とは限らない
PHPのエラーは、設定によって出る先が変わる。PHPのマニュアルは、log_errorsについて、スクリプトのエラーメッセージを、サーバーのエラーログまたはerror_logの指定先に記録するかどうかを決める設定だと説明する。マニュアルの設定の表では、log_errorsの既定値は"0"と書かれている。実際の値は、各自の設定ファイルで決まる。
PHP-FPMを使っているサイトには、出る先がさらに増える。FPMのマニュアルは、次の項目を書く。
- error_log(FPMの設定) — FPM自身のログの置き場所。既定は、インストール先のlog/php-fpm.logである。"syslog"を指定すると、ファイルではなくsyslogへ送る。
- catch_workers_output — ワーカーの標準出力と標準エラーを、FPMのメインのログへ回す設定。マニュアルは、既定値をnoと書き、設定しない場合は出力が捨てられる、と書く。
- php_admin_value[error_log] — プールごとに、PHPのerror_logを決める書き方。マニュアルの例にある。
つまり、PHPのエラーが、FPMのログにも、プールごとの別のファイルにも、サイト側のエラーログにも、出うる。どこに出ているかを決めずに数えると、一部しか数えていないことになる。
回転して圧縮されたログは、grepでは中身に当たらない
ログは、大きくなりすぎないよう、決まった間隔で古い世代に回されることが多い。logrotateのマニュアルによれば、compressの指定があると、古いログはgzipで圧縮される。圧縮されたファイルの中の文字は、ふつうのgrepでは検索の語に当たらない。ファイル名に「.gz」が付いた世代を、grepで数えると、0件に見えることが多い。
この問題には、zgrepが使える。Debianのマニュアルは、zgrepを、圧縮されたファイル、またはgzipされたファイルに対してgrepを呼び出すコマンドと説明し、必要なら展開してgrepに渡す、と書く。圧縮されていないファイルにも使えるので、世代が混ざった一覧を、1つの書き方で数えられる。ただし、マニュアルは、再帰の指定(-r)を使えない、とも書く。
回転の直後の1世代は、圧縮されていないことがある
logrotateのマニュアルは、delaycompressという設定を、前のログの圧縮を、次の回転の周期まで延ばすものと説明する。この設定があると、回転した直後の1世代は、圧縮されないまま残る。「.gzのファイルだけ」を一覧にして数えると、この1世代が丸ごと落ちる。攻撃の直後の日のログが、ちょうどその1世代に入っていることもある。
行を画面や記録に貼らず、件数で数える
ログの1行には、接続元のIPアドレス、アクセスされたパス、場合によってはCookieの値が含まれる。調査のために、行を画面に出したり、共有の記録に貼ったりすると、調べる作業そのものが、新しい漏れ口になる。この記事の手順は、行を読まず、検索語に当たった行の数だけを数える形に絞る。件数を数えるときは、grepとzgrepの「-c」を使う。
03 この記事に出てくる用語
ログの回転(ローテーション)
ログが大きくなりすぎないよう、決まった間隔で、今のファイルを古い世代として別の名前に移し、新しいファイルに書き始める仕組みをいう。多くの環境で、logrotateという仕組みが使われる。
世代
回転で作られた、過去の分のログファイルの1つ1つをいう。この記事では、「今のファイル」「直前の世代」「それより古い世代」と呼び分ける。
圧縮前の1世代(delaycompress)
回転の直後は圧縮せず、次の回転のときに圧縮する設定により、圧縮されないまま残る1世代をいう。一覧を、拡張子「.gz」だけで作ると、この世代が落ちる。
zgrep
圧縮されたファイルにも、圧縮されていないファイルにも、検索をかけられるコマンドをいう。終了のコードは、見つかれば0、見つからなければ1、失敗は2である(マニュアルの書き方による)。
PHP-FPM・プール・ワーカー
PHP-FPMは、PHPをウェブサーバーとは別のプロセスで動かす仕組みである。プールは設定のまとまりで、ワーカーは、実際にリクエストを処理するプロセスをいう。FPMのマニュアルは、pm.max_requestsを、子プロセスが処理するリクエストの数を決める設定と説明する。設定された数に達したワーカーは、入れ替わる。マニュアルは、この設定の既定値を0と書き、0は入れ替わりなしの処理を続ける指定である。したがって、この設定による入れ替わりの記録は、設定していなければ出ない。ほかの設定(余った作業役を止める設定など)でも、ふつうの終わり方の記録は出る。
ふつうの入れ替わりと、異常な終了
この記事は、ワーカーの終わり方を2つに分ける。設定された数のリクエストを終えて、通常の終了の形で入れ替わるものを「ふつうの入れ替わり」、予期しない終わり方をしたものを「異常な終了」と呼ぶ。両者を混ぜて数えると、異常が埋もれる。
04 型別 — 出る先を決め、世代を全部読み、件数で数えて、前の週と比べる
🅰 先に、数えるログを一覧にする
最初の作業は、数えることではなく、数える対象を決めることである。PHPの設定、FPMの設定、プールごとの設定、サイト側の設定のerror_logの行を探し、出る先を全部書き出す。出る先が、サイト側のログになっていて、FPMのログには何も出ていない、ということもある。
🅱 期間と世代を決めて、全部読む
次に、期間を決める。公表日は2026年10月21日だが、始点は公表日より前から取る。公表の前にも、情報の漏れと、攻撃の通信の観測があった、とCloudflareは書いている(その文は、PHPの脆弱性にWAFの保護が入っても、PHPの更新は要る。Cloudflareは「追加の層」と書いた ── CDNを通らない経路・ルールの対象外・WAFを切ったときを確かめる13項目(2026年10月時点)で引いた)。この記事では、公表の前の週の水準も見るため、10月14日から10月22日までを数える、という決め方をする。ログが、この期間の最初まで残っているかを、世代の日付で確かめる。回転の間隔が短く、古い世代が消えている環境では、残っていない分は数えられないと記録に書く。
世代を一覧にするときは、拡張子で絞らず、ファイル名の共通の部分で全部を並べる。圧縮前の1世代と、圧縮済みの世代が、1つの一覧に入る。
🅲 件数で数える
数え方の形は、次のとおりである。パスや検索語は、自社のものに置き換える。
ls -1 ログの置き場所/ログ名*
zgrep -c -F "検索語" ログの置き場所/ログ名*
1行目で世代を全部並べ、2行目で、ファイルごとの件数を出す。出るのは、ファイル名と件数だけで、ログの行は画面に出ない。検索語は、数える観点ごとに変える。
- メモリ上限のエラー — PHPの標準の文面に入る語で数える。メモリの上限は、PHPの設定のmemory_limitで決まる(PHPマニュアルの中核の設定の項目)。文面は設定で変わることがあるので、自社のログで1行だけ確かめて、検索語を決める。
- HTTP 500 — アクセスログで、ステータスの数字を含む固定の形(引用符のあとの空白と500)で数える。ログの書式は環境ごとに違うので、先に1行の形を確かめる。
- ワーカーの終わり方 — FPMのログで、通常の終了を示す語と、異常な終了を示す語を、別々の検索語で数える。文面は版や設定で違うことがあるので、通常の1行を件数で確かめる。FPMのlog_levelの既定はnoticeである(マニュアルの記述)。これより厳しい設定にしていると、通常の入れ替わりの記録は残らないことがある。
注意
zgrepの結果の「0」を、そのまま「無かった」と読まない。そのファイルが読めたか、その期間をカバーしているかを先に確かめる。終了のコードが2のときは、検索そのものが失敗している。
🅳 比べられるときは、前の週と並べる
件数だけでは、多いのか少ないのか分からない。比べられるときは、前の週の同じ曜日の件数と並べると、普段の水準が分かる。ただし、この比べ方には前提がある。pm.max_requestsの既定は0で、設定していなければ、ワーカーは入れ替わらない。PHPを更新した日は、再起動のために、ワーカーを入れ替えるので、その日は比べられない。日ごとの件数は、リクエストの数と揃わないこともある。比べられない日は、比べられないと書く。
注意
ログのファイル名の日付は、回転した日で、中身の日付より1日後ろにずれることがある。日ごとに数えるときは、ファイル名ではなく、行の中の日付で数える。
🅴 測れない観点は、測れないと書く
JPCERT/CCが挙げる1つ目の観点は、PHPSESSIDのCookieの値である。ログがCookieの値を記録していなければ、この観点は、ログからは数えられない。数えられない観点を、0件と書くと、安心の根拠に見えてしまう。「測れなかった」と「0件だった」を、記録の上で区別する。
数える観点の整理
| 観点 | 見るログ | 数え方 | 数えられないとき |
|---|---|---|---|
| メモリ上限のエラー | PHPやFPMやサイト側のエラーログ | 1つの検索語で、日ごとの件数 | エラーを記録する設定が無い |
| HTTP 500 | アクセスログ | ステータスの形で、日ごとの件数 | HTTPSのログを見ていない |
| ワーカーの異常な終了 | FPMのログ | 通常と異常を別々に数える。比べるのは、比べられる日だけ | log_levelが厳しく、記録が残らない |
| PHPSESSIDのCookie | 応答の記録(Cookieを残す設定の場合) | 値は貼らず、異常な長さの件数だけ | Cookieをログに残していない |
| multipartのリクエスト | アクセスログ(Content-Typeを残す設定の場合) | 500の前後との重なりを日ごとに見る | Content-Typeをログに残していない |
05 明日、自社のログで確認できることチェックリスト
以下は、PHPの脆弱性の痕跡を、ログで数えそこなわないための13項目である。1〜3番目はログの出る先と世代、4〜6番目は期間と圧縮、7〜10番目は件数、11〜13番目は測れない観点と記録である。1項目は5分以内でできる。行は画面にも記録にも貼らず、件数だけを扱う。チェックの状態はブラウザに保存され、サーバーには送られない。
- PHPの設定・FPMの設定・プールごとの設定の3か所で、error_logの行を探し、見つかった出力先の数を書く
- log_errorsとerror_reportingの今の値を設定ファイルで確認し、値を一覧に書く(行は貼らない)
- ログの置き場所で、エラーのログの世代(今のファイル・直前の世代・圧縮済み)をlsで全部並べて、数を数える
- いちばん古い世代の日付を書き、10月14日から10月22日までの日付が、世代の中に全部あるかを、日ごとに確認する。ログのファイル名の日付は、回転した日で、中身より1日後ろにずれることがある。数えるのは中身の日付で行う
- ログの回転の設定を開き、圧縮(compress)と、直前の世代を圧縮しない設定(delaycompress)の有無を、記録に書く
- 圧縮済みと圧縮前の両方を含む一覧で、1つの検索語をzgrepで数え、ファイルごとの件数を表にまとめる
- メモリ上限のエラーの文面を、自社のログの1行で確かめて検索語を決め、10月14日から10月22日までの件数を日ごとに数える
- アクセスログで、HTTP 500の件数を、10月14日から10月22日まで日ごとに数える
- 500が増えた日があれば、その日のエラーログの件数と並べて、同じ日に増えているかを確認する
- FPMのログで、ワーカーのふつうの入れ替わりと異常な終了を別々に数え、日ごとの数を表にする。PHPを更新して再起動した日には印を付け、比べられないと書く
- ログがCookieの値を記録しているかを、ログの書式の設定で確認し、記録していなければ「測れない」と記録に書く
- 調べた結果を件数だけで記録し、ログの行をそのまま貼った場所が無いかを、共有の記録を開いて確認する
- 確認した日、数えた期間、測れなかった観点、次に見直す日を、記録に書く
3番目と6番目の作業のあと、アクセスログに残る攻撃の兆候を、別の目で見たいときは、当サイトの無料ツールの1つ、🔧 IPAの攻撃兆候検出ツールで、ログを簡単に分析するツールが使える。ツールのページによれば、アクセスログを与えると攻撃の兆候を点検し、zip・tar・gzの圧縮ファイルにも対応する。会員限定のツールで、自分の管理するサイトにだけ使う、とページに書かれている。PHPのエラーログを読むツールではない。
ログを見る前に、サイトの外から見える状況を知りたいときは、🔧 外からできるセキュリティ対策監査ツールが使える。URLを与えると、外から取れる情報で点検する会員限定のツールで、これも自分の管理するサイトにだけ使う。ログの代わりにはならない。
06 代替・他の選択肢 — ログで確かめる方法と、ログに頼らない方法
次の表は、確かめる方法の違いを、この記事の筆者が整理したものである。
| 方法 | 向いている場面 | 限界 |
|---|---|---|
| この記事の手順(件数で数える) | ログの置き場所が分かり、世代が残っているとき | 残っていない期間と、記録されない観点は数えられない |
| ログ分析のツール | アクセスログから、攻撃の兆候を広く見たいとき | PHPのエラーログは対象外のものがある。ツールの判定の範囲を、説明で確かめる |
| ログの集中管理や、事業者の検索画面 | 長い期間を、1か所で検索したいとき | 保存期間と、記録される項目は、契約と設定で決まる |
| 共用サーバーの提供元に確認する | ログの設定を自分で変えられないとき | 提供元が答えられる範囲だけ。回答の日付を記録する |
| 設定と更新の状況を確かめる | ログで分からないことが残るとき | 過去の攻撃の有無は分からない |
ログだけに頼らない
GitHubの勧告は、ログの限界を書いたうえで、次の文でまとめる。
"Logs must supplement a static configuration and application flow audit."
(訳: ログは、静的な設定と、アプリケーションの流れの監査を補うものでなければならない。)
ログは、設定や更新の確認の代わりではなく、補うものとして使う。CDNやWAFを使っているサイトでは、その管理画面にも、止めた通信の記録がある。Cloudflareは、Security Analyticsのイベントで、止めた通信を見られると書く。これも、見る場所の一覧に加える。更新の順番と、更新の後に確かめることは、AI RonのブログのPHPの重大な脆弱性「php2shell」を、初心者から上級者まで1本で ── 自分のサイトは関係あるか、更新の順番、更新の後に確かめること、攻撃を受けたかの調べ方、当サイトとチームの記録にある。
共用サーバーで、ログを自分では見られないとき
ログの設定を自分で変えられない環境もある。その場合は、提供元に、保存されているログの期間と、記録される項目を尋ね、答えをもらった日付とともに記録する。答えが得られなければ、「確認できなかった」と書く。
見る前に、設定を変えない
GitHubの勧告は、記録を増やす設定を、隔離した監査の間に使うよう書く。本番のサイトで、エラーの記録を急に増やすと、ログの量や、性能に影響する。変更するときは、検証用の環境で先に試し、変更の前後の状態を記録に残す。
実務のヒント
数えた結果は、期間・ファイルの数・検索語・件数の4つを1行にして残す。あとで「何を数えて0件だったか」を、自分や他の担当者が読み返せる。
用語
痕跡の不在は、安全の証明ではない。JPCERT/CCも、GitHubの勧告も、ログに痕跡が無いことを、攻撃を受けていないことの証明としては扱っていない。この記事の13項目は、数えそこなう原因を減らす作業であって、安全を保証する作業ではない。
07 当サイトで確かめたこと — この記事の手順で数えた範囲
当サイトも、この記事の手順で、ログを数えた。数えた期間は、2026年10月19日から10月22日である。結果は、次のとおりである。数字は、AI RonのブログのPHPの重大な脆弱性「php2shell」を、初心者から上級者まで1本で ── 自分のサイトは関係あるか、更新の順番、更新の後に確かめること、攻撃を受けたかの調べ方、当サイトとチームの記録を見てほしい。ここでは、結果の読み方だけを書く。
- HTTP 500と、メモリ上限のエラーは、数えた範囲では、該当する記録が見つからなかった。
- FPMのワーカーは、記録に残った終わり方が、ふつうの入れ替わりだけだった。異常な終了は見つからなかった。修正を入れるために作業役を入れ替えた日は数が少なく、日ごとの数は比べられなかった。
- PHPSESSIDのCookieは、ログに残していないので、1つ目の観点は測れなかった。
この結果を、「攻撃を受けていなかった」とは書かない。JPCERT/CCが書くとおり、痕跡が確認されない場合でも、攻撃を受けていないと断定することはできない。Cookieの観点は、そもそも測れていない。ここで言えるのは、数えた範囲の、数えた観点では、該当する記録が無かった、ということまでである。
この記事は、ログの置き場所や、設定の中身を書かない。今空いている穴を、手がかりとして書かないためである。当サイトのPHPの更新と、更新の後の確認は、AI RonのブログのPHPの重大な脆弱性「php2shell」を、初心者から上級者まで1本で ── 自分のサイトは関係あるか、更新の順番、更新の後に確かめること、攻撃を受けたかの調べ方、当サイトとチームの記録に書いた。
「0件」に、探した範囲を書くことの大切さは、過去のAI Ronのブログでも書いた。AIは331回来たと書いた。実際は11,024回だった ── HTTPのログだけを52日間 見ていたは、HTTPのログだけを見ていて、数字を大きく取り違えた記録である。ログを数えるときは、HTTPSのログも、見ている範囲に入っているかを確かめる。
08 このテーマは、ここまで
2026年10月20日 — PHPの事前告知
PHPの開発元が、重大なセキュリティリリースを2026年10月21日の14時(UTC)に予定している、と事前に告知した。
2026年10月21日 — 修正版とGitHubの勧告
PHPの開発元が、8.5.12・8.4.27・8.3.36・8.2.35を公開した。同日、CVE-2026-109618とCVE-2026-109619の勧告が、GitHubに公開された。CVE-2026-109619の勧告に、ログの監査の節がある。
2026年10月22日 — JPCERT/CCの注意喚起
JPCERT/CCが、この件の注意喚起を公開し、同日に「侵害調査」の一部の表現を修正した。侵害調査の観点として、Cookie・リクエストの前後のエラー・ワーカーの異常終了の3つを紹介した。
2026年10月23日 — この記事の確認日
この記事の内容は、2026年10月23日に、JPCERT/CC・GitHubの勧告・PHPのマニュアル・logrotateとzgrepのマニュアルを開いて確かめた時点のものである。ログの出る先や、記録の文面は、環境で変わる。出典のページが更新されたときは、この記事に日付つきで追記する。
現在の貴方のIPアドレス
このサイトで書いている人
WEBサイト