AWS WAFログ分析してみた(CloudWatch Logs Insights)
はじめに
担当している案件で WAFログを分析する必要があったため、
CloudWatch Logs Insightsを用いて分析してみました。
そもそもWAFログには何が記録されているのか
WAFログ:Web ACLまたはProtection Packで分析されたHTTPリクエストの詳細情報を記録したJSON形式のログ
主要なフィールド:
timestamp: リクエスト処理時刻(ミリ秒単位のタイムスタンプ)
formatVersion: ログフォーマットのバージョン
webaclId: Web ACLまたはProtection Packの識別子(GUID)
terminatingRuleId: マッチした終端ルールのID
terminatingRuleType: ルールのタイプ(REGULAR、RATE_BASED、GROUP、MANAGED_RULE_GROUP)
action: 実行されたアクション(ALLOW、BLOCK、COUNT、CAPTCHA、CHALLENGE)
clientIp: クライアントのIPアドレス(フィールド名はclientIp)
httpRequest: HTTPリクエストの詳細情報
WAFログの分析方法
-
CloudWatch Logs Insights
手軽に始められる基本ツール
AWS 標準のログ分析ツールで、SQL 風のクエリ構文でログを検索できます。結果はすぐにテーブルで可視化でき、設定も簡単で低コストです。日常的な監視や報告書の作成に最適で、初心者にもおすすめです。 -
Amazon Athena
大量データの詳細分析用
S3に保存された大量のログを詳しく分析できるサービスです。過去数ヶ月分のデータをまとめて処理し、複雑な統計分析が可能です。ただし、リアルタイム性は低く、データが準備されるまで時間がかかります。長期的な傾向分析や年次レポート作成に向いています。 -
OpenSearch Service
本格的な監視システム用
高度な検索機能と可視化ダッシュボードを提供する専門ツールです。リアルタイム監視やアラート機能が充実していますが、設定が複雑で運用コストも高くなります。24時間体制でのセキュリティ監視が必要な大規模組織向けです。
CloudWatch Logs + OpenSearch Service 統合の選択肢もありそうです。
今回はシンプルで簡単なcloudwatchlogs insightを利用することにしました。
CloudWatch Logs Insightsのクエリを使ってログを分析
1.【cloudwatch】コンソールから【ログのインサイト】を選択
2.【期間】や【タイムゾーン】をフィルターし、【ロググループ】を選択
3.クエリ構文を入力して【クエリの実行】を押下

すぐにテーブルを作成してくれます。結果のエクスポートも可能です。
基本的なクエリ構文
CloudWatch Logs Insightsでは以下の基本構文を使用します:
fields [フィールド名1], [フィールド名2]
| filter [条件]
| stats [集計関数] by [グループ化フィールド]
| sort [ソート条件]
| limit [行数制限]
ブロックされたリクエストの詳細情報を時系列で取得する
fields
@timestamp,
terminatingRuleId,
httpRequest.httpMethod as method,
httpRequest.uri as uri,
httpRequest.country as country,
httpRequest.clientIp as clientIp
| parse @message '{"name":"User-Agent","value":"*"}' as userAgent
| parse @message '"ruleId":"*",' as ruleId
| filter action='BLOCK'
| sort @timestamp desc
| display @timestamp, terminatingRuleId, ruleId, method, uri, country, clientIp, userAgent
| limit 100
取得するフィールド:
@timestamp: ブロックされた日時
terminatingRuleId: ブロックしたルールのID
httpRequest.httpMethod: HTTPメソッド(GET、POSTなど)
httpRequest.uri: アクセス先のURL
httpRequest.country: アクセス元の国
httpRequest.clientIp: アクセス元のIPアドレス
parse @message '{"name":"User-Agent","value":"*"}' as userAgent
:ログメッセージからUser-Agent情報を抽出
parse @message '"ruleId":"*",' as ruleId
:ログメッセージから詳細なルールIDを抽出
条件・並び順
filter action='BLOCK': ブロックされたリクエストのみに絞り込み
sort @timestamp desc: 新しい順(降順)で並び替え
limit: 取得数制限
最もアクセスが多いURL(リクエスト先)のランキングを取得する
fields httpRequest.uri
| stats count(*) as requestCount by httpRequest.uri
| sort requestCount desc
| limit 10
クエリの詳細:
処理の流れ
fields httpRequest.uri: URIフィールド(アクセス先URL)を選択
stats count(*) as requestCount by httpRequest.uri: URI別にリクエスト数をカウント
sort requestCount desc: リクエスト数の多い順で並び替え
limit 10: 上位10件のみ表示
取得できる情報:
人気のページ・エンドポイント: 最もアクセスされているURL
攻撃対象の傾向: 攻撃者が狙いやすいパス
トラフィック分析: サイト全体のアクセスパターン
国別のリクエスト数ランキング取得
fields httpRequest.country
| stats count(*) as requestCount by httpRequest.country
| sort requestCount desc
| limit 10
IPアドレス別のリクエスト数ランキング取得
fields clientIp
| stats count(*) as requestCount by httpRequest.clientIp
| sort requestCount desc
| limit 10
参考
Discussion