こんにちは。X(クロス)イノベーション本部 ソフトウェアデザインセンター セキュリティグループの耿です。
AWS WAF は簡単に Web アプリに WAF を追加でき、かつ値段も他の WAF 製品より安いため、好きな AWS サービスの一つです。そんな AWS WAF ですがしばらく構築・運用し、これを最初から知っておけば・・・と思ったことがあるので 8つご紹介します。
AWS WAF の基本については分かっている前提で、特に説明はいたしません。また2023年10月現在の最新バージョンである、いわゆる「AWS WAF v2」を対象としています。
- その1: AWS マネージドルールのボディサイズ制限が厳しい
- その2: ファイルアップロードが AWS マネージドルールの XSS に引っかかることがある
- その3: マネージドルールにはバージョンがある
- その4: CloudWatch Logs のロググループ名に決まりがある
- その5: 35個のログストリームに分割される
- その6: ログに Cookie ヘッダーが記録されてしまう
- その7: ログ出力条件の EXCLUDED_AS_COUNT とは何か
- その8: コンソールで作成したルールを JSON 出力すると IaC での書き方がわかる
- さいごに
その1: AWS マネージドルールのボディサイズ制限が厳しい
AWS WAF を利用する際に AWS マネージドルールはとても便利です。その中のコアルールセット (CRS) マネージドルールグループには SizeRestrictions_BODY
というリクエストボディのサイズを検査するルールがあり、8 KB を超えるボディを持つリクエストをブロックします。ボディサイズが大きいリクエストや、ファイルアップロードにおいては 8 KB を簡単に超えてしまうことがあるので、本番環境に導入する前に想定するサイズのリクエストがブロックされないかしっかりテストが必要です。
※このようなルールが設定されている理由は、AWS WAF はリクエストボディの最初の 8 KB しか検査できないという制約があるためです。リクエストボディの 8 KB 以降の位置に攻撃文字列が含まれていても検査できないため、コアルールセットでは 8 KB を超えるリクエストを一律でブロックすることで対応しています。
※(2024/3/11追記)リクエストボディの検査サイズ制限が 16 KB(64 KBに引き上げ可能)に増えました。https://aws.amazon.com/about-aws/whats-new/2024/03/aws-waf-larger-body-inspections-regional-resources/
想定する正当なリクエストが 8 KB を超えてしまう場合、ルールグループ内の SizeRestrictions_BODY
ルールのアクションを「Count」にオーバーライドすることで、実質的に除外することができます。ただしボディサイズ制限が全くないのは心許ないので、次のように WAF Web ACL を作ることが考えられます。
- コアルールセットマネージドルールグループでは
SizeRestrictions_BODY
を「Count」にオーバーライドする - 適切な値でボディサイズ制限を行う独自ルールを作成する
なお、re:Post の次の投稿でも SizeRestrictions_BODY
でブロックされた場合の対応方法が記載されています。マネージドルールに一致した際に追加されるラベルと独自ルールを組み合わせて、特定の URI パス(=ファイルアップロードを行う URI パス)ではない場合のみブロックを発動させる方法です。
AWS WAF によってブロックされているファイルをアップロードするにはどうすればよいですか?
https://repost.aws/ja/knowledge-center/waf-upload-blocked-files
その2: ファイルアップロードが AWS マネージドルールの XSS に引っかかることがある
Web アプリに対してバイナリファイルのアップロードをした時に、運悪くコアルールセット (CRS) マネージドルールグループの CrossSiteScripting_BODY
ルールに引っかかってしまったことがありました。そのときのログの一部は次の通りです。バイナリデータが XSS のシグネチャに一致してしまったようです。
"terminatingRuleMatchDetails": [ { "conditionType": "XSS", "location": "BODY", "matchedData": [ "<", "\u0000\u0000\u0000\u0000\u0000\u0000\u0000\u0000" ] } ],
re:Post の次の投稿によると、ファイルアップロードでは CrossSiteScripting_BODY
以外にも SQLi_BODY
、WindowsShellCommands_BODY
、GenericLFI_BODY
、SizeRestrictions_BODY
ルールによってブロックされる可能性があるようです。
AWS WAF によってブロックされているファイルをアップロードするにはどうすればよいですか?
https://repost.aws/ja/knowledge-center/waf-upload-blocked-files
投稿では対応方法として「文字列または正規表現 (regex) 一致条件が設定されたセーフリストを使用して、リクエストを許可します。」が紹介されています。マネージドルールに一致した際に追加されるラベルと独自ルールを組み合わせて、リクエストボディに特定の文字列(ファイル拡張子などを想定していると解釈しました)が含まれていない場合のみブロックを発動する方法です。(注:日本語の投稿ではなぜかリクエストヘッダーから特定の文字列を探すとありますが、英語の投稿ではリクエストボディから探すと書いてあります。リクエストボディの方がしっくりきています。)
あるいは「その1」の SizeRestrictions_BODY
の場合と同じように、特定の URI パス(=ファイルアップロードを行う URI パス)ではない場合のみブロックを発動させる方法を取っても良さそうです。
その3: マネージドルールにはバージョンがある
マネジメントコンソールでマネージドルールを使っていたらすぐに気が付いたと思うのですが、筆者は CDK で Web ACL を作っていたのでずっと知りませんでした。マネージドルールにはバージョンが存在します。
(AWS マネージドルールのうち「IP レピュテーション、Bot Control、アカウント乗っ取り防止のルールグループ」にはバージョンが存在しません。バージョンに関わらず、日ごろから頻繁にルールが更新されるためでしょう。)
バージョンを特に指定しない場合、そのマネージドルールがデフォルトと設定したバージョンが利用され、ルールプロバイダーがデフォルトバージョンを変更すると構築した Web ACL でも自動的に新しいバージョンに更新されます。自動的に更新されることを避けたい場合は、「静的バージョン」を明示的に指定します。ただしこの場合も、「緊急時の必須の更新」がされる可能性があるそうです。またバージョンの有効期限が切れると、次のような扱いになるようです。
マネージドルールには SNS トピックが用意されている場合があり、サブスクライブすることでバージョンを含めたルールの更新情報を受け取ることができます。特に静的バージョンを使用する場合はルールバージョンを手動更新することになるので、SNS トピックをサブスクライブしておくと良いでしょう。
その4: CloudWatch Logs のロググループ名に決まりがある
AWS WAF を利用する場合はログをぜひ取っておきたいところですが、CloudWatch Logs に出力する場合、ロググループ名は aws-waf-logs-
で始まる必要があります。
https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/logging-cw-logs.html#logging-cw-logs-naming
マネジメントコンソールでロググループを指定する場合は画面にこの条件が書いてありますし、 aws-waf-logs-
で始まるロググループ以外は選択肢に表示されないのでまだ分かりやすいです。
しかし CloudFormation (もちろん CDK も)を利用する場合、aws-waf-logs-
で始まるロググループ以外を Web ACL に関連付けしようとすると、デプロイ時に次のようなエラーになります。「ARN が有効でない」というエラーメッセージでは何がいけないのか全く分からず、どハマりしたことがあるのは自分だけではないはずです。
Resource handler returned message: "Error reason: The ARN isn't valid. A valid ARN begins with arn: and includes other information separated by colons or slashes., field: LOG_DESTINATION, parameter: arn:aws:logs:ap-northeast-1:111122223333:log-group:waf-logs-xxxxx
ちなみにログの出力先が S3 バケットの場合も、バケット名は aws-waf-logs-
で始まる必要があります。
https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/logging-s3.html#logging-s3-naming
その5: 35個のログストリームに分割される
CloudWatch Logs にログを出力すると、35個のログストリームに分割され、この数を減らすことはできません。出力のスループットを高めるためのようです。
近い時刻で発生したログメッセージでも、複数のログストリームに分散して出力されるため、ロググループから特定のログを探すのは難しいです。ログストリームを横断して時系列にログを見たり、特定のログを探したい場合は CloudWatch Logs Insights を利用するのが便利です。以下の公式ブログや re:Post 投稿が参考になります。
Amazon CloudWatch Logs による AWS WAF ログの分析
https://aws.amazon.com/jp/blogs/news/analyzing-aws-waf-logs-in-amazon-cloudwatch-logs/
CloudWatch または Amazon S3 に保存されている AWS WAF ログを分析するオプションは何ですか?
https://repost.aws/ja/knowledge-center/waf-analyze-logs-stored-cloudwatch-s3
その6: ログに Cookie ヘッダーが記録されてしまう
WAF Web ACL のログを出力すると、httpRequest.headers
フィールドにリクエストヘッダーが記録されます。つまり、クライアントが送信した Cookie も記録されてしまいます。
(スクリーンキャプチャの値はダミーです)
例えば分析のためにログを出力したり、共有したりすると有効なセッション ID などセンシティブな情報が漏えいするリスクがあります。
この問題への対応として、ログ出力時に特定フィールドをマスキングすることが可能です。例えば cookie ヘッダーをマスキングするには次のように設定します。
このように設定すると、出力されるログの cookie ヘッダーは REDACTED
という文字列に置き換えられます。
その7: ログ出力条件の EXCLUDED_AS_COUNT とは何か
ログを出力する場合、アクションが特定の条件に該当する場合のみ出力するように設定できます。リクエストが許可された場合のログも全て記録すると量が多くなってしまうので、「ブロック」時と「カウント」時のみ出力したいことがあります。
カウント時の条件は、実は COUNT
と EXCLUDED_AS_COUNT
の2種類があります。基本的には COUNT
扱いと考えて良いですが、 EXCLUDED_AS_COUNT
として扱われるケースが2つ、以下のブログで説明されています。
AWS WAF のログ分析に関する考慮事項
https://aws.amazon.com/jp/blogs/news/aws-waf-log-analysis-considerations/
- マネージドルールグループで ”Set all rule actions to count” で Count に設定した場合
- マネージドルールグループで個別のルールを Count に設定した場合
ただし上の AWS のブログは情報が古く、現在個別のルールを Count に設定した場合の動きは少々異なるようです(参照)。簡単にまとめると、以下のようになると理解しています:
- 2022 年 10 月 27 日より前に、
- WAF ルールの JSON では
ExcludedRules
が利用でき、これに含めた個別ルールはEXCLUDED_AS_COUNT
扱いになる - マネジメントコンソールで個別ルールを Count に設定すると、
EXCLUDED_AS_COUNT
扱いになる
- WAF ルールの JSON では
- 2022 年 10 月 27 日以降は、
この動きを意識してログ出力条件を作成すると良いと思います。
その8: コンソールで作成したルールを JSON 出力すると IaC での書き方がわかる
CloudFormation もしくは CDK で複雑な条件の Web ACL を作成する場合、書き方が分からずに困ることがあります。
例えば「URIパスが /file/upload
以外へのリクエストでボディサイズが 100 KB 以上の場合にブロックする」ルールは、CDK だとこんな感じで書くことになります。(ルール部分のみ)
{ name: "SizeConstraint", priority: 10, statement: { andStatement: { statements: [ { notStatement: { statement: { byteMatchStatement: { searchString: "/file/upload", fieldToMatch: { uriPath: {}, }, textTransformations: [ { priority: 0, type: "NONE", }, ], positionalConstraint: "EXACTLY", }, }, }, }, { sizeConstraintStatement: { fieldToMatch: { body: { oversizeHandling: "CONTINUE", }, }, comparisonOperator: "GE", size: 100 * 1000, textTransformations: [ { priority: 0, type: "NONE", }, ], }, }, ], }, }, action: { block: {} }, visibilityConfig: { sampledRequestsEnabled: true, cloudWatchMetricsEnabled: true, metricName: "SizeConstraint", }, },
これをヒントなしで正しく書ける気がしません。一方でマネジメントコンソールでルールを作成すれば、画面の説明に従って割と分かりやすく作成できます。
そこで IaC で WAF のルールを作る場合も、まずはマネジメントコンソールで試しに作成してみて、画面で「ルール JSON エディタ」に切り替えましょう。JSON でのルールの書き方を表示してくれます。これをコピーして IaC で利用すると良いでしょう。(CDK に使う場合はプロパティ名の頭文字を大文字から小文字に変換する必要がありますが、自前で書くよりはずっと楽です。)
ちなみにルール JSON エディタ画面の説明にもある通り、JSON を使えばビジュアルエディタではサポートされていないような、深い階層の条件を持つルールを作ることもできます。以下の re:Post 投稿も参考になります。
複雑なカスタム AWS WAF JSON ルールを作成する方法を教えてください。
https://repost.aws/ja/knowledge-center/waf-create-complex-custom-rules
さいごに
ご紹介した内容はいずれも公式ドキュメントやブログに書いてあることなのですが、WAF を使い始める時は早く構成したい気持ちが先走ってなかなか網羅的にチェックはできませんね。今振り返ると最初に知っておきたかったと感じたことをまとめてみました。誰かの参考になれば嬉しいです。
記事の中に掲載したリンクで特におすすめのものを再掲しておきます。
- コアルールセット (CRS) マネージドルールグループ
- https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/aws-managed-rule-groups-baseline.html#aws-managed-rule-groups-baseline-crs
- (どのようなルールで検査を行うのか、利用前に一通り把握しておきましょう)
- AWS WAF によってブロックされているファイルをアップロードするにはどうすればよいですか?
- Amazon CloudWatch Logs による AWS WAF ログの分析
- CloudWatch または Amazon S3 に保存されている AWS WAF ログを分析するオプションは何ですか?
- ルールグループ内のアクションオーバーライド
- 複雑なカスタム AWS WAF JSON ルールを作成する方法を教えてください。
以下は、この記事では触れませんでしたが参考となる re:Post の投稿です。
- AWS WAF ルールでブロックされているファイルのアップロードを、ルールを除外せずに明示的に許可するには、どうすればよいですか?
- AWS WAF の HTTP リクエストの XSS または SQLi 検査から特定の URI を除外する方法を教えてください。
- AWS WAF を利用して DDoS 攻撃を緩和するにはどうすればいいですか?
私たちは同じチームで働いてくれる仲間を大募集しています!たくさんのご応募をお待ちしています。
セキュリティエンジニア執筆:@kou.kinyo、レビュー:寺山 輝 (@terayama.akira)
(Shodoで執筆されました)