コンテンツへスキップ

アクセスログの読み方 — 404で終わる探索と、見落としたくない「200で返った不審なパス」

公開しているサイトには、毎日のように「何かを探しに来る」リクエストが届く。ほとんどは存在しないファイルを探して 404 で終わるが、ログを読む習慣がないと、その中に混ざった本当に確認すべき1行に気づけない。この記事では、WordPress のアクセスログを読み取り専用で眺めるときの見方を、「日常的な探索」と「確認したい異常」を分ける考え方を中心に整理する。ログを見ることは安全を請け合うものではなく、気づくための手がかりを増やす作業として読んでほしい。

アクセスログに記録されるもの

Web サーバーは、受けたリクエストを1行ずつ記録する。多くのサーバーで使われる「combined 形式」では、おおむね次の要素が並ぶ。

接続元 - - [日時] "メソッド パス プロトコル" ステータス 送信バイト "参照元" "ユーザーエージェント"

補足: ステータスは HTTP の応答コードのこと。200 は「正常に返した」、404 は「該当するものがない」を表す。詳しくはHTTPステータスコードの記事を参照。

ログの置き場所や、ダウンロード・閲覧の方法は、ホスティング事業者によって違う。管理画面から取得する形式のことも多いので、事業者の案内を先に確認する。また、ログは一定期間で古いものが消える(ローテーション)。調べたい時期のログが残っているかも合わせて確認したい。

日常的に届く「探索」は、たいてい404で終わる

公開サイトのログを初めて見ると、自分では作っていないパスへのリクエストが多いことに驚く。設定ファイルの控えのような名前、環境情報を表示する診断用ファイルのような名前、管理ツールの入口のような名前 — 「ありそうなファイル名」を機械的に順に当てていくだけの探索で、特定のサイトを狙ったものとは限らない。

こうした探索の大半は、そのファイルが存在しないので 404 で終わる。つまり、ログに探索の痕跡があること自体は、異常とは限らない。ここで大切なのは、次の2点を分けて考えることだ。

  • 探索が来ていること: 公開サイトでは日常的。これだけで慌てる必要はない
  • 探索に対して 200(正常)で返していること: 探されたものが実際に存在し、中身が返った可能性がある。こちらが確認対象になる

まず全体の分布を見る

個別の行を読む前に、ステータスの内訳を数える。読み取りだけのコマンドで済む(access.log は取得したログのファイル名の例)。

awk '{print $9}' access.log | sort | uniq -c | sort -rn

200 が大半で、次に 301/304、404 が少し、という形が典型だが、サイトの性質で大きく変わる。大切なのは普段の形を一度知っておくことだ。普段と比べて 404 が急に増えた、あるいは 5xx が目立つ、といった変化は、確認のきっかけになる。

404の多いパスを眺める

次に、404 で終わったパスを多い順に並べる。

awk '$9==404 {print $7}' access.log | sort | uniq -c | sort -rn | head -30

見るポイントは次のとおり。

  • 自分のサイトに存在しないはずの名前ばかりであれば、日常的な探索である可能性が高い。そのまま眺めるだけでよい
  • 自分のサイトにかつて存在した URL が上位に来ているなら、リンク切れや移転漏れの可能性がある。これは運用上の改善点だ
  • 同じパスへ短時間に大量に来ている場合は、サーバー負荷の観点で気に留めておく

見落としたいのは「200で返った不審なパス」

本題はここだ。404 の一覧は「外れた探索」の記録だが、当たってしまったものは 200 で記録される。次のように、PHP ファイルへのリクエストのうち 200 で返ったものを数える。

awk '$9==200 && $7 ~ /\.php/ {print $7}' access.log | sed 's/?.*//' | sort | uniq -c | sort -rn | head -30

WordPress の通常運用でよく出る PHP は限られている(index.php、wp-login.php、wp-cron.php、xmlrpc.php、admin-ajax.php など。構成により異なる)。それ以外の名前の PHP が 200 で返っていたら、「それは何のファイルか」を説明できるかを確認する。

特に確認したい組み合わせがある。

  • uploads ディレクトリ配下の PHP が 200 で返っている: 画像などを置く場所で PHP が動いているのは、通常の運用では想定しにくい。侵害点検の記事で触れた「uploads の PHP 確認」と合わせて、ファイルの存在を調べる
  • POST リクエストが、見覚えのない PHP に 200 で返っている: 確認の対象になる
awk '$6=="\"POST\"" && $9==200 {print $7}' access.log | sed 's/?.*//' | sort | uniq -c | sort -rn | head -30

どちらも、見つかったからといって侵害されたと断定するものではない。プラグインが独自の PHP を持っていることもあるし、自分で置いた診断用ファイルを忘れていることもある。確認の順序は、「何のファイルか」→「いつ誰が置いたか」→「中身は想定どおりか」だ。判断がつかない場合は、ファイルを消す前に、まず現状を記録して保全する。

「200」だけでは判断できない理由

ログの読み方には、いくつか限界がある。

  • 存在しないページに 200 を返す構成がある: テーマやプラグイン、サーバー設定によっては、存在しないパスにもエラー表示のページを 200 で返すことがある(いわゆるソフト 404)。この場合、200 の行が必ずしも「当たり」ではない。実際にそのパスを開いて、何が返るかを確認する
  • CDN やリバースプロキシを経由していると、接続元が事業者側のものになる: ログの接続元から相手を判断できないことがある
  • ログに出ていないものは、起きていないとは限らない: ログが残る期間、記録される項目、取得できる範囲には限りがある。「ログに無い=何も無かった」と読まない
  • ログは起きたことの記録で、防ぐ仕組みではない: 見るだけでは何も守れない。気づいた後の対応(更新、設定の見直し、点検)につなげて初めて意味が出る

習慣にするための最小の形

毎日すべてを読む必要はない。続けやすい最小の形として、次のような流れが考えられる。

  1. 週に一度など決めた頻度で、ステータスの内訳を数える(普段の形との違いを見る)
  2. 200 で返った PHP のうち、見覚えのないものを一覧にする
  3. 気になるものがあれば、ファイルの実在・更新日時・中身を確認する
  4. 確認の結果と判断を、短くメモに残す(次に同じ行を見たとき、説明できるようにするため)

ログレベルとローテーションの記事で整理したように、ログは「あとから確認できる形で残っていること」が価値になる。自分の運用に合わせて、保存期間と確認の頻度を決めておくとよい。

まとめ

  • 公開サイトへの探索リクエストは日常的に届く。大半は 404 で終わり、それ自体は異常とは限らない
  • 確認したいのは、探されたものが実際に存在して 200 で返っていないか。とくに uploads 配下の PHP と、見覚えのない PHP への POST
  • 200 の行は「当たり」の可能性を示すだけで、ソフト 404 の構成もある。ファイルの実在と中身で判断する
  • ログは気づくための材料であって、安全を示すものではない。更新・点検・設定の見直しと組み合わせて使う

アクセスログは、手を動かす前の「状況確認」にあたる。攻撃面を狭めるチェックリストのように入口を減らす見直しと、ログで入口の様子を確かめる習慣は、互いを補う関係にある。