Web サーバのアクセスログから不審な IP アドレスを抽出し、AI を使ってアクセスログ解析レポートと WAF・IP 制限の対応策レポートを生成するツールを作りました。
ポイントは「ログを AI に丸投げしない」設計です。Python で事前にログを解析・集計し、不審 IP の候補やステータス分布、アクセスされたパスを整理したうえで、その結果だけを AI に渡します。AI には、状況を説明する解析レポートと、AWS WAF や mod_security でどう防御するかをまとめた対応策レポートを生成させます。この記事では、なぜそういう設計にしたのか、どう実装したのかをまとめます。
このツールの主な機能
- Web サーバのアクセスログを Python で前処理し、IP ごとの集計・不審 IP の判定まで行う
- 集計結果を AI に渡してセキュリティレポートを生成する
- アクセスログ解析レポートと WAF・IP 制限の対応策レポートの 2 種類を生成
- Markdown と HTML の両形式で出力
- Apache, Nginx, AWS ALB など複数のログフォーマットに対応
- ローテート済みログや複数サーバのログをまとめて解析できる
- Anthropic / OpenAI / Ollama の複数 AI プロバイダーに対応
- 調査時間帯・除外 IP・良性ボットを設定ファイルで管理
なぜ AI にログをそのまま渡さないのか
最初にやりたかったことは単純でした。Web サーバのアクセスログを見て、特定の時間帯にアクセスが集中している IP や、不審なリクエストを送ってきている IP を見つけることです。
では、ログを AI に渡せばよいかというと、それには問題があります。
- ログ全体を渡すとトークン数が大きくなりすぎる
- 生ログを全部読ませても、AI が重要な部分に集中できない
- 毎回の解析結果が安定しにくい
- IP ごとの件数やステータス分布は、Python で集計した方が正確かつ確実
- API コストと処理時間が増える
そこで、AI は「ログを読む係」ではなく、「Python が整理した集計結果をもとにレポートを書く係」として使う方針にしました。
機械的にできる集計・判定は Python で行い、文章化・要約・対応策の整理を AI に任せる、という役割分担です。
ツールの全体構成
最終的なデータの流れは次のようになっています。
config.yamlから調査時間帯・除外 IP・ログフォーマット・AI 設定を読み込む- ログフォーマットから正規表現とフィールドマッピングを生成する
- 1 つまたは複数のログファイルを Python で解析する
- IP ごと・時間帯ごとにアクセスを集計する
- ルールベースで不審 IP を抽出する
- 集計結果を AI 用テキストに整形する
- AI でアクセスログ解析レポートを生成する
- 解析レポートをもとに WAF・IP 制限の対応策レポートを AI で生成する
- Markdown または HTML として
reports/に保存する
生成できるレポートは 2 種類で、それぞれ Markdown と HTML の両形式で出力できます。
Python による前処理
前処理では次の処理を順に行います。
- 設定ファイルを読み込む
- ログフォーマットに応じた正規表現を構築する
- ログを 1 行ずつパースし、クライアント IP・時刻・HTTP メソッド・パス・ステータスコード・User-Agent を抽出する
- 除外 IP や良性ボットを取り除く
- 指定した時間帯に含まれるアクセスだけを集計する
- IP ごとのアクセス数・パス・ステータス・UA をまとめる
- 不審 IP をルールベースで判定する
- AI に渡しやすいテキストに整形する
調査時間帯で絞り込む
config.yaml には調査したい時間帯を複数指定できます。障害や不審アクセスが発生したと思われる時間帯を中心に、前後 30 分から 1 時間程度を設定します。
Python 側でログのタイムスタンプを datetime に変換し、対象外の時間帯は AI に渡しません。関係のないログを最初の段階で除外することで、処理量とコストを両方抑えられます。
除外 IP と良性ボット
内部監視サーバーやロードバランサーのヘルスチェックは、解析対象から外したいアクセスです。設定ファイルで除外 IP と良性ボットの User-Agent を指定できます。
exclude_ips:
- "192.168.1.1"
good_bots:
- "ELB-HealthChecker"
- "UptimeRobot"
これにより、正常な監視アクセスや内部アクセスを除外し、見るべき外部アクセスだけに絞れます。
IP ごとの集計
IP ごとに次の情報を集計します。
- アクセス回数
- HTTP メソッド別件数
- パス別件数
- ステータスコード別件数
- User-Agent の種類
「ある IP が 1 時間に 100 回以上アクセスしている」「404 を大量に出している」「/wp-login.php や /.env にアクセスしている」といった情報を Python 側で取り出し、AI にはこの集計済み一覧を渡します。
不審 IP のルールベース判定
不審 IP の判定も AI には任せず、まず Python でルールベース判定を行います。現在は主に次の条件でフラグを立てています。
- 高頻度アクセス:調査時間帯内に 100 件以上
- 404 多発:30 件以上かつ 404 の割合が 30% 超
- スキャン疑いのパス:
admin・wp-・.env・.git・phpmyadmin・passwdなどを含む URL - 単一 UA 大量アクセス:50 件以上かつ User-Agent が 1 種類のみ
AI に不審判定そのものを委ねるのではなく、Python 側で候補と理由を整理してから渡します。AI はその結果をもとに、読みやすい日本語のレポートにまとめます。
ログフォーマットへの対応
最初は特定の Apache ログ形式を前提にしていましたが、環境によってフォーマットが違うため、設定ファイルで指定できるようにしました。現在は次の形式に対応しています。
- Apache Combined Log Format
- Apache の
LogFormatを直接指定する形式 - Nginx の
log_formatを直接指定する形式 - AWS ALB ネイティブログ
- ログ収集ツールが付与した先頭フィールドのスキップ
ロードバランサー配下では Apache の %h が実クライアント IP ではなく内部 IP になることがあります。そのため client_ip_field を指定して X-Forwarded-For などを実クライアント IP として扱えるようにしました。
log_format:
format: '%h - - %t "%r" %>s %b "%{X-Forwarded-For}i" "%{Referer}i" "%{User-Agent}i"'
client_ip_field: "%{X-Forwarded-For}i"
環境ごとのログ形式の違いは、コードではなく設定ファイル側で吸収する設計です。
AI に渡すデータの整形
Python の前処理が終わると、AI 向けのテキストに整形します。時間帯ごとにアクセス数上位の IP を表形式に近い形でまとめ、不審 IP については次の情報を添付します。
- IP アドレス
- 合計アクセス数
- 不審と判定した理由
- ステータス分布
- User-Agent
全期間のアクセス上位 20 IP も参考情報として追加します。
max_ips_per_window で AI に渡す IP 件数を制限しているため、ログ量が多い場合でも上位 IP に絞ってプロンプトが肥大化するのを防げます。
この段階で AI に渡す情報は、生ログそのものではなく、調査対象時間帯に絞った集計結果です。AI は何万行ものログを読むのではなく、Python が抽出した「見るべき IP」「主なパス」「ステータス分布」「不審と判定した理由」を読みます。
これにより、トークン数を抑えられるだけでなく、AI の出力も安定します。ログ解析の事実確認は Python が担当し、AI はその事実をもとに説明文や対応方針を組み立てる役割に集中できます。
生成する 2 種類のレポート
1. アクセスログ解析レポート
Python が集計したアクセス状況をもとに、何が起きていたのかを説明するレポートです。主な内容は次のとおりです。
- 調査概要と結論
- 時間帯別アクセス数上位
- 不審 IP の詳細と攻撃手法の分類
- 全期間の上位 IP 一覧
- 推奨対応
単なる一覧ではなく、「この IP はスキャンの可能性がある」「404 が多く、存在しない URL を探っているように見える」といった説明を AI に書かせます。
2. WAF・IP 制限の対応策レポート
先に生成した解析レポートを入力として、どのように防御すべきかをまとめる資料です。
対応策レポートは、Python の集計結果から直接作るのではなく、先に生成したアクセスログ解析レポートを入力として作成します。まず AI が「何が起きていたのか」を解析レポートとして整理し、その内容をもとに、別のテンプレートで「どう防御するか」をまとめます。
原因分析と対策提案を分けて扱うことで、読み手は状況把握用の資料と、実際の設定作業に使う資料を分けて確認できます。
- ブロック対象 IP の候補と制限の考え方
- WAF ルール案の提案
- 各対策の有効性と限界
- 即時対応と恒久対応の切り分け
- 設定例・コマンド例
WAF タイプは --waf オプションで指定します。
python3 ai_report.py --waf aws
python3 ai_report.py --waf mod_security
AWS WAF の場合は IP セットルール・レートベースルール・マネージドルールグループを含め、mod_security の場合は SecRule の例と OWASP Core Rule Set の利用方針を含む対応策レポートを生成します。実際に適用するルールは、業務影響や誤遮断の可能性を確認したうえで判断します。
Markdown と HTML の両出力
最初は Markdown のみでしたが、Word や PDF に変換すると見た目に不満が出ることがあったため、HTML 直接出力も追加しました。
python3 ai_report.py --format markdown
python3 ai_report.py --format html
出力されるファイルは次の 4 種類です。
| ファイル | 内容 |
|---|---|
reports/access_log_analysis.md | アクセスログ解析レポート(Markdown) |
reports/waf_ip_restriction.md | WAF 対応策レポート(Markdown) |
reports/access_log_analysis.html | アクセスログ解析レポート(HTML) |
reports/waf_ip_restriction.html | WAF 対応策レポート(HTML) |
HTML モードでは、CSS 付きの完全な HTML 文書を生成します。テンプレート内に <!-- INSERT: xxx --> のようなコメントを置き、AI にその部分を実際の内容で置き換えさせます。
HTML 出力は Markdown を変換しているのではなく、CSS 付きの HTML テンプレートを AI に渡し、完成した HTML 文書として直接生成させています。
AI によっては ```html のようなコードフェンスを付けて返すことがあるため、Python 側で HTML 部分だけを取り出す処理も入れています。
対応する AI プロバイダー
Anthropic API と OpenAI API のほか、Ollama にも対応しました。
python3 ai_report.py --api anthropic
python3 ai_report.py --api openai
python3 ai_report.py --api ollama --model llama3.2
外部 API を使う場合は、Python で抽出・集計したログ由来の情報を AI に渡します。外部 API に情報を送りたくない場合は、ローカルで動作する Ollama を選択できます。コストを抑えて試したい場合にも有効です。
外部 API を使う場合でも、送信するのは生ログ全体ではなく前処理済みの集計結果です。それでも IP アドレスやアクセスパスなどの情報は含まれるため、扱うログの内容に応じて利用する AI プロバイダーを選ぶ必要があります。
作ってみてわかったこと
一番重要だったのは「AI に全部やらせない」という設計判断です。
アクセスログ解析では、件数の集計・時間帯の抽出・ステータスコードの分布・URL パターンの検出など、プログラムで確実に処理できる部分が多くあります。そういう部分は Python で処理した方が正確です。
AI に任せるべきなのは次の部分です。
- 集計結果の意味づけ
- セキュリティ観点からの説明
- レポートとしての構成
- WAF 設定例の整理
- 運用担当者が読みやすい文章化
Python が事実を整理し、AI が文章と判断材料をまとめる。この役割分担にしたことで、ログ全体を AI に投げるよりも安定して実用的なレポートを作れるようになりました。
AI を使った業務自動化や、アクセスログの調査・障害分析にお困りの場合は、AI 活用支援または障害調査・原因分析からお気軽にご相談ください。