2026年5月29日
2026年5月29日
Apacheのログから攻撃を見つける方法|grep・awkでの解析とfail2banでの自動ブロック
Web サーバーへの攻撃の多くは、Apache のアクセスログとエラーログに痕跡が残ります。この記事では、ログの場所と読み方、攻撃の痕跡を探すコマンド、見つけたパターンを fail2ban で自動ブロックする設定を順に説明します。fail2ban の設定項目は、2026年11月時点で fail2ban の公式リポジトリにある設定ファイルで確認したものです。
ログの場所と形式
| OS | アクセスログ | エラーログ |
|---|---|---|
| Debian・Ubuntu | /var/log/apache2/access.log |
/var/log/apache2/error.log |
| RHEL・AlmaLinux・Rocky Linux | /var/log/httpd/access_log |
/var/log/httpd/error_log |
バーチャルホストごとに CustomLog や ErrorLog で別のファイルを指定している場合は、そのファイルを見ます。設定の場所は grep -R "CustomLog\|ErrorLog" /etc/apache2/(RHEL 系は /etc/httpd/)で確認できます。Ubuntu ではログを読むのに root 権限か adm グループが必要です。
アクセスログは多くの場合 combined 形式で、1行は次のように並びます。
203.0.113.5 - - [10/Nov/2026:10:15:32 +0900] "POST /wp-login.php HTTP/1.1" 200 4521 "-" "Mozilla/5.0 ..."
空白で区切ると、1番目がアクセス元の IP アドレス、4番目が日時、6番目がメソッド(先頭に " が付く)、7番目がパス、9番目がステータスコードです。以下の awk の例はこの並びを前提にしています。LogFormat を変えている場合や、先頭にホスト名が付く vhost_combined 形式では番号がずれるので合わせてください。
ローテーション済みの古いログは access.log.1、access.log.2.gz のような名前で残ります。圧縮されたファイルは zcat や zgrep で読みます。
アクセスの多い IP アドレスを調べる
まず、どこから多くのアクセスが来ているかを見ます。
# アクセス数の多い IP アドレス上位20件
sudo awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
# 404 を多く返している IP アドレス(存在しないファイルを探し回るスキャンの目安)
sudo awk '$9 == 404 {print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
# 特定の時間帯だけ見る(例: 11月10日の10時台)
sudo grep '10/Nov/2026:10:' /var/log/apache2/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head
アクセス数が多いだけで攻撃とは限りません。検索エンジンのクローラーや監視サービスも上位に来るので、次の項目のようにアクセス先と組み合わせて判断します。
攻撃によく見られるパターン
WordPress のログイン画面への総当たり
wp-login.php と xmlrpc.php への POST が短い時間に繰り返されている場合は、パスワードの総当たりの可能性があります。
sudo grep -E '"POST /(wp-login|xmlrpc)\.php' /var/log/apache2/access.log \
| awk '{print $1}' | sort | uniq -c | sort -rn | head -20
設定ファイルや管理ツールを探すスキャン
公開してはいけないファイルや、よく知られた管理ツールのパスへのアクセスです。ステータスが 200 になっているものがあれば、そのファイルが実際に外から読めていないかを確認してください。
sudo grep -iE '"(GET|POST|HEAD) /[^ ]*(\.env|\.git/|wp-config\.php\.|phpmyadmin|\.sql|\.bak)' \
/var/log/apache2/access.log | awk '{print $1, $7, $9}' | sort | uniq -c | sort -rn | head -30
SQL インジェクション・ディレクトリトラバーサル・XSS の試行
パスやクエリ文字列に SQL の構文、../、<script> などが含まれるアクセスです。URL エンコードされた形(%20、%2e%2e、%3Cscript)も探します。
sudo grep -iE 'union(\+|%20| )+select|information_schema|\.\./|%2e%2e|<script|%3Cscript' \
/var/log/apache2/access.log | awk '{print $1, $7, $9}' | head -50
不審なアップロード先へのアクセス
wp-content/uploads/ 以下の .php ファイルが呼ばれている場合は、アップロードされたバックドアが使われている可能性があります。ステータスが 200 なら、そのファイルが実在するかをすぐに確認してください。
sudo grep -E '/wp-content/uploads/[^ ]*\.php' /var/log/apache2/access.log | awk '{print $1, $7, $9}'
エラーログを見る
エラーログには、アクセス制限で拒否されたリクエストや、PHP のエラー、mod_security の検知などが記録されます。普段と比べて急に増えたメッセージがないかを見ます。
# 最新のエラーを追いかけて表示
sudo tail -f /var/log/apache2/error.log
# アクセス制限で拒否されたリクエスト
sudo grep "client denied by server configuration" /var/log/apache2/error.log | tail -20
fail2ban で自動的にブロックする
fail2ban は、ログを監視して条件に合うアクセスを繰り返す IP アドレスを、ファイアウォールで一定時間ブロックするソフトウェアです。Debian・Ubuntu では次のコマンドで入ります(RHEL 系は EPEL リポジトリから入れます)。
sudo apt update
sudo apt install fail2ban
標準のフィルターを使う
fail2ban には Apache 用のフィルターが用意されています。/etc/fail2ban/jail.conf に設定がありますが、すべて無効になっています。jail.conf は更新で上書きされるので直接編集せず、/etc/fail2ban/jail.local に有効にしたいものだけを書きます。
| jail 名 | 見るログ | 検知する内容 |
|---|---|---|
apache-auth |
エラーログ | Basic 認証などの認証失敗 |
apache-badbots |
アクセスログ | 既知の悪質なクローラーの User-Agent |
apache-noscript |
エラーログ | 存在しないスクリプトの実行要求 |
apache-overflows |
エラーログ | 異常に長いリクエスト |
apache-botsearch |
エラーログ | 管理ツールなどを探すアクセス |
# /etc/fail2ban/jail.local
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
# 信頼できる接続元(自分のIPアドレスなど)はブロックしない
ignoreip = 127.0.0.1/8 ::1
[apache-auth]
enabled = true
backend = auto
[apache-badbots]
enabled = true
backend = auto
ログの場所は Debian 系・RHEL 系それぞれの既定値が fail2ban 側に設定されているので、標準の場所なら logpath は書かなくてかまいません。backend = auto は、ディストリビューションの初期設定でログの読み方が systemd のジャーナルになっている場合に、ログファイルを読むよう明示するためのものです。
WordPress のログインへの総当たりを止めるフィルター
wp-login.php と xmlrpc.php への POST を数えるフィルターを作ります。
# /etc/fail2ban/filter.d/wordpress-login.conf
[Definition]
failregex = ^<HOST> -[^"]*"POST /(?:wp-login|xmlrpc)\.php
ignoreregex =
このフィルターはログインの成功・失敗を区別せず POST の回数だけを数えるので、findtime の間に maxretry 回を超えると、正しいパスワードで何度もログインした人もブロックされます。サイトの使い方に合わせて回数を調整してください。
# /etc/fail2ban/jail.local に追記
[wordpress-login]
enabled = true
backend = auto
port = http,https
filter = wordpress-login
logpath = /var/log/apache2/access.log
maxretry = 10
findtime = 10m
bantime = 1h
有効にする前に、fail2ban-regex でフィルターが手元のログに一致するかを確かめます。
sudo fail2ban-regex /var/log/apache2/access.log /etc/fail2ban/filter.d/wordpress-login.conf
結果の「Failregex」の件数が 0 なら、ログの形式とフィルターが合っていません。
反映と確認・解除
# 設定を反映
sudo systemctl restart fail2ban
# 動いている jail の一覧
sudo fail2ban-client status
# jail ごとのブロック中の IP アドレス
sudo fail2ban-client status wordpress-login
# 誤ってブロックした IP アドレスを解除
sudo fail2ban-client set wordpress-login unbanip 203.0.113.5
fail2ban の設定全体の流れは「fail2ban で不正アクセスを自動ブロックする設定手順」でも説明しています。
mod_security でリクエストの中身を検査する
ログの解析と fail2ban は、攻撃が記録されたあとに対処する方法です。リクエストの内容を受け付ける時点で検査するには、Apache のモジュールの mod_security(ModSecurity)と、そのルール集の OWASP Core Rule Set(CRS)を使います。Debian・Ubuntu では libapache2-mod-security2 パッケージで入り、インストールするとモジュールが有効になります。
導入直後の設定は SecRuleEngine DetectionOnly(検知してログに記録するだけ)にしておき、正常なアクセスが誤って検知されないかをログで確認してから On(遮断する)に切り替えます。WordPress の管理画面は誤検知が起きやすいので、この確認を省かないでください。導入手順とルールの書き方は「Apacheのmod_securityでWAFを設定する方法」で説明しています。
運用の注意
catで大きなログ全体を表示すると端末が流れて読めません。grep・awkで絞り込むか、lessで開いてください。X-Forwarded-Forを使うロードバランサーや CDN の後ろにある場合、ログの1番目の欄はプロキシの IP アドレスになります。その場合はmod_remoteipで本来の接続元を記録するよう設定してから解析・ブロックしてください。プロキシの IP アドレスをブロックすると、すべての利用者がアクセスできなくなります。- ログは日々増えるので、logrotate で保存期間を決めておきます。設定は「Apacheのログをlogrotateでローテーションする方法」を参考にしてください。
- 見つかった攻撃が成功していた形跡(
uploads内の PHP が 200 を返している、知らない管理者が増えているなど)がある場合は、ブロックだけでなく侵入の調査が必要です。