こんにちは。TalentXのエンジニアチームでSREを担当する前野です。
今回は、AWS WAFを運用する上で基本的なログの有効化からCloudWatch Logs Insightsを使用したログの実践的な分析方法を紹介します。
AWS WAFのログの有効化手順
AWS WAFではWeb ACLごとにトラフィックを記録するログを出力できます。
出力先は、"CloudWatch"、"Firehose"そして"S3"から選択できますが、ここではお手軽にログの分析ができるCloudWatchを選択します*1。
Cloud Watchロググループの作成
事前準備として配信先のロググループを作成します。
注意点として、ロググループ名は"aws-waf-logs-"から始まる必要があります*2。
prefixの後に続く名前は何でも良いですが、どのACLのトラフィックログを配信するか明確にするためにWeb ACLと同名にするのがおすすめです。ログの有効化
以下、具体的な手順を記載します。(1)ロググループの選択
WAFのACLの画面から「Logging and Metrics」タブを選択し、「Enable」ボタンを押下するとログの設定画面に遷移します。
ログの配信先として「CloudWatch Logs log group」を選択し、先ほど作成したロググループを選択しましょう。

ロググループの選択 (2)フィルターの設定
「Filter logs」の項目で出力するログを制限できるフィルター機能が利用できます。
特にアクセス量の多い本番環境では、WAFのCloudWatchに対して送信するログの量が膨大になり、PutLogEvents利用料*3が高くなるため必要に応じて設定してください。一例として、WAFを紐付けるALBやAPI Gatewayでアクセスログを取得している場合は、下記のようにAllowしたトラフィックは記録せず、その他のトラフィック(BlockやClount)のみ記録することでログの取り込み量を削減できます。

フィルターの設定画面 また、ラベルを指定してログに記録することで、本番環境にルールを追加する前に既存のトラフィックに影響があるか容易に分析できます。
例えば、AWSのマネージドなルールセット(AWSManagedRulesCommonRuleSet)をCountアクションで設定した後に、リクエストボディにクロスサイトスプリクティングの攻撃文字列を含むトラフィックが発生した場合は、ログにラベルawswaf:managed:aws:core-rule-set:CrossSiteScripting_Bodyが付与されて出力されます*4。
Condition typeに「Request has a label」、Condition valueに上記ラベル名を指定することで、トラフィックを許可する場合でも攻撃リクエストに該当するトラフィックをログに記録できます。

フィルターの条件設定 (3)設定の保存
最後に「Save」を押下して設定を保存します。

ログ設定画面 1~2分程度で設定が反映され、ロググループにログが出力されます。
WAFのログ形式
実際に出力されたログを見てみましょう。
ログはJSON形式で出力されます。
{ "timestamp": 1632420429309, "formatVersion": 1, "webaclId": "arn:aws:wafv2:us-east-1:123456789012:regional/webacl/captcha-web-acl/xxxx", "terminatingRuleId": "Default_Action", "terminatingRuleType": "REGULAR", "action": "ALLOW", "httpSourceName": "APIGW", "httpSourceId": "123456789012:b34myvfw0b:pen-test", "(略)" }
各フィールドの説明は公式ドキュメントに掲載されています*5。
CloudWatch Logs Insightsによる分析
出力されたログをより容易に分析するツールがCloudWatch Logs Insightsです。
まず上記で作成したロググループを選択して画面上部の「Logs Insightsで表示」を押下します。
ここで指定のクエリを実行することで、条件に一致するログを抽出できます。
WAFを運用する上で特に使用頻度が高いクエリを紹介します。
なお、検索対象の期間は画面上部のメニューにて指定することができます。

Blockされたトラフィックを抽出する
fields @timestamp, @message, @logStream, @log | filter action = 'BLOCK' | sort @timestamp desc | limit 10トラフィックに対するWAFの操作はactionフィールドを参照することで分かります。
「=(イコール)」演算子を使用することで、actionがBLOCKのトラフィックのみ抽出できます。ラベルを指定して抽出する
fields @timestamp, @message, @logStream, @log | filter @message like 'awswaf:managed:aws:core-rule-set:CrossSiteScripting_Body' | sort @timestamp desc | limit 10特定のルールに違反したかどうかを検索する場合には、ラベルを指定して抽出する方法が便利です。
ここではログの全ての内容を含む「@message」に対して正規表現「like」を用いて、指定のラベルに該当する文字列を抽出します。URIパスごとに集計する
fields @timestamp, @message, @logStream, @log | stats count() as uriCount by httpRequest.uri | display uriCount, httpRequest.uri | sort uriCount desc | limit 10特定のパスに対する攻撃リクエストの発生頻度を調査する場合など、URIパスごとのトラフィックを集計する場合には、URIパスが含まれる「httpRequest.uri」フィールドに対して「stats count()」関数を用いることで実現できます。
まとめ
本記事ではAWS WAFのログ記録を有効化する方法と、出力したログをCloudWatch Logs Insightsで分析する例を紹介しました。
いずれもWAFの運用を行う上で基本かつ効果的な方法ですので、WAF運用に携わる方のご参考になれば幸いです。
最後に、TalentXでは共に働くエンジニアメンバーを募集しており、AWS等のクラウド領域のスキルを持つ方も大歓迎です。
ご興味ある方は下記リンクの求人をご覧ください。
talentx.brandmedia.i-myrefer.jp