IP制限が効かない?執拗なボットをAIと撃退した話
[Slug: bot-defense-fail2ban-query-rewrite]
最近、休日はギターの指板を眺めながらアルペジオの運指練習をするのが日課になっていますが、
そんな趣味の時間を遮るように、突然サーバーの高負荷アラートが鳴り響きました。
自分の公開しているブログやWikiのアクセス数が、普段の数十倍へと急増していたのです。
個人開発やWebサイトを運営していると、海外からの不審なアクセスやスキャン行為に常にさらされることになります。
今回は、従来の単純なIP制限が一切通用しなかった執拗なボット攻撃に対して、
AIとログを解析し、わずか数分で効果的なクエリ制限と自動ブロックの仕組みを構築した実体験をまとめました。
結論からいうと、Apacheの mod_rewrite によるクエリ(要求パラメータ)の制限と、
Fail2Ban による動的なIP一時遮断を組み合わせた多重防御が非常に有効でした。
この記事を読むことで、世界中から分散して届くしつこいスキャンアクセスから、
サーバーの負荷とログデータの精度を即座に守る具体的な設定コードとノウハウが手に入ります。
1. 昔の常識が通用しない「いたちごっこ」の状況
「またいつものスキャンか。IPアドレスをふさげば終わりだな」
最初はよくある単発のスキャンアクセスだと思い、IPアドレスを個別にブロックすれば解決すると考えていました。
昔であれば、特定の国や、ログに残った不審なIPアドレスをサーバー設定で拒否リストに入れれば、アクセスの急増は沈静化していました。
しかし、今回は違いました。
攻撃元のIPアドレスを調べて拒否リストに登録しても、数時間後にはすぐ隣のIPアドレスや、全く別のプロバイダーからアクセスが届きます。
/24(256個のIPアドレスの束)という範囲でブロックを広げても、翌日にはさらに別のIP帯からすり抜けてくる状態でした。
敵はIPアドレスを次々に変更しながら、執拗にこちらのWikiのログイン画面やシステムページを叩き続けていました。
サーバーの負荷が高まり、高負荷による「504 Gateway Timeout(接続タイムアウト)」が発生し始め、
IPアドレスの追加登録だけでは対応が追いつかず、根本的な対策が必要となりました。
2. 敵の正体:世界中の無数のPCが入れ替わり立ち替わり叩く
ログを詳しく解析した結果、このアクセス急増の仕組みが判明しました。
攻撃者は、単一のパソコンから攻撃しているのではなかったのです。
世界中の無数の一般家庭用回線やホスティングサーバー(いわゆるボットネット)を乗っ取り、
そこから「1台につき1回だけ」交互にアクセスを投げつけていました。
これでは、同じIPアドレスから何度もアクセスされたときに検知する一般的な防壁(Fail2Banなどの標準設定)は、完全にすり抜けてしまいます。
もしかすると、攻撃している側もプログラムを用いて、ブロックされたことを検知して自動で次のIPへシフトするような仕組みにしているのかもしれません。
【ボット側のリアルタイム制御】自動でIPをシフトする仕組み
この「超分散型」の動きは、あらかじめ用意されたリストをただ順番に叩いているのではなく、
攻撃側のシステムによってリアルタイムで自動制御されていたと考えられます。
ボットネット(攻撃の司令塔となるサーバー)は、標的のサーバーから返ってくるレスポンス(ステータスコード)をリアルタイムに監視しています。
- IPのローテーション: 同じIPアドレスからのアクセスが閾値(1分間に10回)に達しないよう、毎秒のように接続元IPを「IP(A) ➡ IP(B) ➡ IP(C)」と機械的に切り替えながらリクエストを投げます。
- ブロック(403エラー)を検知した自動シフト: 特定のIP帯をブロックすると、そのIPからの接続で
403 Forbiddenエラーが発生します。ボットのプログラムはこれを検知した瞬間に「このIPはブロックされた」と判断し、即座に隣のIP帯(今回の例では 47.128.25.0/24 から 47.128.26.0/24 へ)や、別のAWSインスタンスへと攻撃命令を自動で切り替える(シフトする)のです。
これが、IPアドレスをブロックしても数時間後には新しいIPから攻撃がすり抜けてきていた「いたちごっこ」の正体でした。
3. 【AIとの対話で得た知識】そもそもなぜこんなに攻撃されるのか?
対策を検討するなかで、AIとログを解析し、現代のインターネットにおけるセキュリティ攻撃の背景について以下の通り整理しました。
① なぜこんなに執拗に攻撃してくるのか?
結論からいうと、これは特定のサイトを狙い撃ちしているわけではなく、「インターネット上の全サーバーを対象に自動プログラム(ボット)が巡回し、ログインできそうな場所を片っ端から攻撃している」状態です。
攻撃者の主な目的は以下のようなものです。
- サーバーの乗っ取り(踏み台化): パスワードを総当たりして管理者権限を奪い、そのサーバーを使って大量のスパムメールを送信したり、別のサイトへDDoS攻撃を仕掛ける「踏み台」や仮想通貨のマイニングに利用します。
- SEOスパム(被リンク埋め込み): Wikiやブログのコメント欄・編集機能を悪用して、自分たちの怪しいサイト(賭博やフィッシング詐欺サイトなど)へのリンクを自動で書き込み、検索エンジンの順位を上げようとします。
一度ボットのデータベースに「ログインページがあるサイト」として登録されてしまうと、世界中のボットネットから入れ替わり立ち替わり執拗にアクセスされ続けることになります。
② どこのウェブサイトもこんなもの?
はい、インターネットに公開されているすべてのサーバーが、漏れなく同様の攻撃(スキャン)にさらされています。
新しいサーバーを立ち上げてグローバルIPアドレスを割り当てると、数分〜数時間以内に最初の海外からのスキャンアクセスが届くのが、現在のインターネットの日常です。
特に、世界中で広く使われている WordPress や Wiki(MediaWikiなど) は構造が公開されているため、ボットにとって「攻撃マニュアルが既に揃っているターゲット」になりやすく、攻撃が集中しやすくなります。
4. 【技術解説】分散攻撃ログの分析と最初の盾(Fail2Ban)
今回の攻撃ログ(Apache)を詳しく見てみると、以下のような特徴がありました。
- Commonログ形式の罠: アクセスログが簡易な
Commonフォーマットで出力されていたため、User-Agent(ブラウザ情報)やReferer(参照元URL)が記録されておらず、ブラウザ情報によるボットの選別が不可能な状態でした。 - PHPプロセスの枯渇: ログには正常応答(200 OK)に交じって、大量の
504 Gateway Timeoutが発生していました。ボットの同時リクエスト数が多すぎて、バックエンドのPHP-FPMの処理能力を超え、サーバーが高負荷になっていました。
そこで、まず稼働させた最初の盾が、サーバーに常駐していたセキュリティツール Fail2Ban です。IPアドレス単体の過剰なバーストアクセスを検知し、OSのファイアウォール(カーネルレベル)で接続自体を遮断します。
以下のカスタムJailを追加し、「1分間に10回以上のアクセスがあったIPを24時間自動で締め出す」設定を行いました。
/etc/fail2ban/filter.d/apache-wiki.conf(フィルター定義)
[Definition] failregex = ^<HOST> - - \[.*\] "GET /wiki/.* HTTP/.*" (200|206|504)
/etc/fail2ban/jail.d/apache-wiki.local`(Jail定義)
[apache-wiki] enabled = true port = http,https filter = apache-wiki logpath = /var/log/httpd/access_log findtime = 60 maxretry = 10 bantime = 86400 ignoreip = 127.0.0.1/8 ::1
しかし、これだけでは防ぎきれませんでした。
ボットネット側がIPアドレスを細かく分散させ、「1つのIPあたり1分間に数回だけ」アクセスを投げるようにシフトしてきたためです。
5. 【最終解決】Apache RewriteRuleによるクエリパターン遮断
IPアドレスの登録や、アクセス回数ベースのブロックが通用しないのであれば、対策は1つです。「アクセス要求の中身(クエリ)」でボットをあぶり出し、一律で遮断することでした。
ログを精査すると、ボットのアクセスには特定の規則性があることが分かりました。
Wikiのシステムページやログイン画面を狙う際、通常の読者は指定しない自動巡回用のクエリパラメータを大量に付与していたのです。
具体的には、title=Special:Userlogin(ログイン画面)や日本語の title=特別:ログイン(URLエンコードされた文字)に対して、returntoquery= や mobileaction=toggle_view_mobile、printable=yes(印刷用表示)などを執拗に組み合わせていました。
そこで、Apacheの設定ファイルに mod_rewrite を使用した以下のクエリ制限ルールを適用しました。
/etc/httpd/conf.d/wiki.conf(追加ルール)
<IfModule mod_rewrite.c>
RewriteEngine On
# 1. ログイン/システム画面へのアクセスで、自動化パラメータを含む場合を即時拒否 (403)
# 対象:ログイン関連 + 自動巡回用パラメータ(returntoquery, mobileaction, printable等)の組み合わせ
RewriteCond %{QUERY_STRING} (returntoquery=|mobileaction=toggle_view_|printable=yes) [NC]
RewriteCond %{QUERY_STRING} (login|Userlogin|%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3|%E7%89%B9%E5%88%A5) [NC]
RewriteRule ^index\.php$ - [F,L]
# 2. メンバーの投稿記録やログなどの大量クロール行為を直接拒否 (403)
# 対象:投稿記録/ログURL + 大量取得パラメータ(limit=500, dir=prev, offset等)の組み合わせ
RewriteCond %{QUERY_STRING} (title=.*(%E6%8A%95%E7%A8%BF%E8%A8%98%E9%8C%B2|%E3%83%AD%E3%82%B0|Contributions|Log).*) [NC]
RewriteCond %{QUERY_STRING} (limit=500|dir=prev|offset=) [NC]
RewriteRule ^index\.php$ - [F,L]
</IfModule&pre>
※ URLデコード前の日本語エンコード文字列(%E7%89%B9%E5%88%A5=特別、%E3%83%AD%E3%82%B0%E3%82%A4%E3%83%B3=ログイン)にもマッチするように記述しています。
6. 防御が正しく機能しているかの検証と、数値の劇的な変化
この一連の対策の「前後」で、防御が正しく動いているか、サーバー上のステータス確認コマンド(fail2ban-client status apache-wiki)を使って検証しました。
このコマンドを実行すると、以下のような現在の検知とブロックの状況がリアルタイムで表示されます。
Status for the jail: apache-wiki
|- Filter <-- 【検知の状況】
| |- Currently failed: 8 # 現在(直近1分間)の不審なアクセス数
| |- Total failed: 672 # 起動してからの不審なアクセスの累計
| `- File list: /var/log/httpd/access_log
`- Actions <-- 【ブロックの状況】
|- Currently banned: 0 # 現在、ファイアウォールでブロック中のIP数
|- Total banned: 0 # これまでにブロックしたIP数の累計
`- Banned IP list: # 現在ブロックされている具体的なIPリスト
特に注目すべきは、検知した怪しいアクセス数を示す Currently failed と、実際にブロック(遮断)されたIPアドレスの数を示す Currently banned です。
対策適用の「前」:IPが分散されてすり抜ける状態
Apacheのクエリ制限を導入する前、Fail2Banのみを有効にしていた時点でのステータスは以下のようになっていました。
Currently failed: 82Currently banned: 0
直近1分間に 82件 もの怪しいリクエストが届いているにもかかわらず、ブロックされたIPは 0 でした。
これはまさに、攻撃者が無数の異なるIPから「1台につき1回だけ」交互にアクセスを投げていた証拠です。同じIPアドレスからのアクセスが閾値(1分間に10回)に達しないため、IP制限の網をすり抜けてしまっていたのです。
対策適用の「後」:アクセスそのものが力尽きる状態
Apacheにクエリブロックのルールを追加し、設定を反映した後のステータスは以下のように劇的に変化しました。
Currently failed: 8Currently banned: 0
直近の不審なリクエスト数(Currently failed)が、82件から わずか8件 へと激減しました。
また、依然として Currently banned は 0 のままですが、これは問題ありません。なぜなら、届いた8件のアクセス自体、すでに前線であるApacheのクエリ制限によって 403 Forbidden として門前払いにされている ためです。
ボット側が「いくらアクセスしても即座にエラーが返ってくる」と諦めてリクエスト頻度自体を落とし、さらに届いたわずかなアクセスもPHP(Wikiエンジン)を動かす前にサーバー層で確実に遮断できているため、Fail2Ban(ファイアウォール)の出番がないほど平和な状態を構築できたのです。
7. テクノロジーの進化と、新しい時代の防御のカタチ
インターネットの世界は、昔に比べて明らかに危険で、攻撃の手口も高度になっています。
しかし同時に、個人のインフラエンジニアがAIアシスタントの協力を得てログを解析し、わずか数分で効果的な防御ルールを組み立てて適用できる、非常に興味深い時代になりました。
攻撃側が自動化されているなら、守る側もAIと共にリアルタイムで防衛策を組んで立ち向かう。
そのような新しい形のサーバー防衛を体験した2日間でした。
めでたし。
