🧇

AWS WAF 導入時に考えた5つのこと

2023/03/06に公開

はじめに

先日新たにAWS WAFを導入をする機会があり、その際に改めて知ったWAFの知見について、より実用的な項目に絞ってまとめてみました。

いまや非常に多くの種類があるWAFルールの情報や、マネージドルールの便利さについて言及された技術ブログはよく見かけます。しかし導入時の設定内容のポイントや、「そもそも何を検討すべきか?」をまとめた記事は多くないと感じていたため、自分で書くことにしました。

※ 以降ではすべてAWS WAFv2を想定した話であり、v2を省略してAWS WAFと書いています。

1. WAFルール・ルールグループの選択

AWS WAFの導入時に最初に考えることとして、ルールの選定や紐付けるリソース、動作モード、ログ出力などがあります。
ルールの選定は、つまるところ 「WAFで何がしたいのか?」 を決定することに等しく、多数のルールグループの中から、最適な複数のルールグループを選択する重要な作業です。実際には、AWSのマネージドルールの中で何が良いのか、他のサードパーティのルールが良いのかなど、公式ドキュメントやMarketplaceの情報などを参照しながらワークロードに合わせて選択します。近年ルールグループやルールは充実しており、優先順位も含めてルールの組み合わせは無限に近くあると思われます。

ルール・ルールグループはアップデート速度が早く、情報がすぐに劣化してしまうため、本記事ではあまり触れません。
参考までに、2023年3月時点でのAWSのマネージドルールグループを列挙しておきます。
これらルールグループの中に、さらにそれぞれのWAFルールが包含されています。

1点、AWS WAF独自の概念として、WCUというキャパシティーの仕組みが有ります。このWCUはルール選択時に制約として機能する側面があるため、必ずしも希望のルール全てを Web ACL (後述)に設定できない可能性もあります。しかし、多くのルールを使わない初期の段階ではあまり気にしなくても問題はありません。

2. 適用対象のAWSリソース

続いて、対象のAWSリソースについて。
WAFに紐付けられるAWSリソースとしては、2023年3月現在以下のものがあります。

  • CloudFront
  • Amazon API Gateway REST API
  • Appliction Load Balancer
  • AWS AppSync GraphQL API
  • Amazon App Runner service
  • Amazon Cognito user pool

例えば、Classic Load Balancer、Network Load Balancer、そしてAPI Gateway HTTP APIなどはWAFv2の適用対象外のため、紐付けることはできません。CloudFrontのみ扱いが異なり、別のWeb ACLが必要(scopeがREGIONAL ではない)なのですが、他の項目はまとめて1つのWeb ACLに紐付けることが可能なため、同一のルールを適用する場合には非常に管理しやすくなります。

3. WAFのログ設定

WAFのログ出力について。出力先は

  1. Amazon S3
  2. CloudWatch Logs
  3. Amazon Kinesis Data Firehose

から選択することになります。実はそれぞれメリット・デメリットがあり、可能であれば導入時に見極めて選択したいところです。

たとえば、S3の場合にはログの分析にAthenaを利用することができ、SQLで手軽に分析が可能です。一方のCloudWatch Logsを選択した場合、CloudWatch Logs Insights を利用することで、分析ができます。しかしこのCloudWatch Logs InsightsはSQLではなく専用のクエリ言語が求められるため若干のクセがあり、SQLに慣れている場合、かつAthenaの費用が許容できる場合にはS3が良いかもしれません。

ただし個人的にはCloudWatch Logs推しで、理由は単純にS3が公開可能なオブジェクトストレージだからです。これは、S3バケットの管理に対してあまり良いイメージを持てない宗派に自分が属しているからですね...(?)

ログ出力の設定に際して注意すべき点は特に無いのですが、バケット名、ロググループ名、Firehoseの名前のいずれにおいてもプレフィックスに aws-waf-logs- が必要です。

https://aws.amazon.com/jp/premiumsupport/knowledge-center/waf-turn-on-logging/

また、出力されたログを分析する時には、下記のAWS公式ブログが非常に有用なので、是非一度は読んでおきたいです。次に説明するアクションの話や、様々な注意事項、Ahtena / CloudWatch Logs Insights それぞれのクエリ例が紹介されています。

https://aws.amazon.com/jp/blogs/news/aws-waf-log-analysis-considerations/

4. ルールアクション(モード)

ログの出力先が決まったら、次は「ルールアクション」を理解する必要があります。AWS WAFには、以下のActionがあります。(厳密にはこれら以外にCAPTCHAもあります。)

  • ALLOW
  • BLOCK
  • COUNT

BLOCKは、文字通りアクセスを遮断するためのモードで、COUNTは遮断は行わないが検査対象には含めるといった動作で、検証に利用することが可能です。このCOUNTアクションは、名称こそ違えど比較的多くのWAFに備え付けられている一般的な機能です。例えばGoogle CloudのWAF、Cloud Armorだとプレビューモード と呼ばれる同様の機能が存在します。

そして、実はAWS WAFにはこのCOUNTアクションに加え

  • EXCLUDED_AS_COUNT

というアクション(?)が存在します。細かいところではログの出力形式などが異なるため、ちょっとわかりにくい印象ですが、COUNTに限りなく近いモードと考えて良さそうです。

実はCOUNTアクションを設定した時に、マネージドルールによってはデフォルトでCOUNTではなく、EXCLUDED_AS_COUNTとしてログが出力されるため、しばしば誤認されやすいポイントです。

しかしながら、ルールグループごとOverrideする設定が用意されているため、この部分にチェックを入れることでCOUNTに統一することが可能です。意図したルールのログが集計できないと言った際には、この点を確認してみてください。

5. 誤検知対応

ログの確認

WAFの誤検知対応は、一般的なWAFを運用する上で避けては通れない道なのでしょうが、AWS WAFの場合でもポイントがあります。おそらくもっとも考慮すべき点は、一部のルールにおいて検知に関する情報が必ずしも完全な形でログ出力されない点ではないかと思われます。

具体的に見た方が早いので、ログのサンプルを例示します。下記は、SQLインジェクションのルールに該当した(COUNTモードで検出された)際のログから抜粋したものです。

... snip ...

"nonTerminatingMatchingRules": [
      {
        "ruleId": "AWS-AWSManagedRulesSQLiRuleSet",
        "action": "COUNT",
        "ruleMatchDetails": [
          {
            "conditionType": "SQL_INJECTION",
            "location": "BODY",
            "matchedData": [
              "username=",
              "fuga-user",
            ],
            "sensitivityLevel": "LOW"
          }
        ]
  	  }
  ]

... snip ...

ログ全体を見れば、IPアドレスやアクセス国などがわかり、さらに上記"nonTerminatingMatchingRules"セクションから

  1. SQLインジェクションルールに該当し

  2. COUNTアクションが実行されたこと、そして

  3. HTTP Bodyで"matchedData""username=""fuga-user"が検知され

  4. Sensitivity LevelはLOWである

ということがわかります。

しかし逆に言うと、これ以上の検知理由や条件についての詳細情報は分かり得ないです。もしCOUNTモードであれば、アクセスは遮断されずリクエストが正常に届きAPIのログを見ることでヒントが得られるかもしれません。しかしBLOCKモードで遮断された場合には他に参照できる情報源がなさそうです。

この時、仮にusernameが他のリクエストにも含まれているのであれば、fuga-userが検知理由(COUNTされた理由)ではないかと「推測」できると思います。そしてもし誤検知であった場合、この推測を元に開発環境や検証環境で確認した上で、除外設定を追加する必要があります。

概ね問題ないはずですが、しかしログ出力が完全ではないため、username以外の要素も含んでいる場合に「fuga-userが果たして本当に検知理由なのか?」、「どう除外設定をすれば必要十分なのか」、状況によってはすぐに確定的な判断ができない場合もあります。

しかしこの不完全なログ出力について、WAFを提供する立場に立って考えてみると、検知理由を含めたすべての情報を開示することはWAFルールの仕様を一部推定できてしまうことにもなりえ、セキュリティ上当然の仕様なのかもしれません。(とは言え、検証環境でも再現できない場合には、WAF運用者にとって非常に悩ましい問題ではあります。)

誤検知の防止 - Scope Down Statement

次に、具体的な誤検知対応の方法として、検査対象を絞るScope Down Stateという便利な機能について説明します。
AWS WAF運用における誤検知対応には、IPアドレスのホワイトリストを作成するといった方法もありますが、他にも条件付けにより検査対象リクエストを絞るScope Down Statementが活用できます。実際的な例を通してこの機能を紹介していきます。

https://docs.aws.amazon.com/ja_jp/waf/latest/developerguide/waf-rule-scope-down-statements.html

ここでもし設定したい内容が(Statementが)シンプルであれば直感的に表現できるため、先に具体例を示します。以下は、WAFの検査対象を/hogeに対象を絞る際のScope Down Stateの一例です。

一方で、複数のStatementを組み合わせる場合には少し工夫が必要です。例えば、複数の否定条件の論理積を適用したい場合、それぞれの否定(Negate Statement resultsにチェックを入れる)を記述した上で、ORでつなぐ必要があります。抽象的に書くと、not (A and B) をそのまま表現することができないため、ド・モルガンの法則に従い、(not A) or (not B) の形式に変換して設定します。

この仕組を誤検知の防止のために活用したいと思います。例えば「fuga-user/hoge にアクセスする場合のリクエストのみを検査対象から除外したい」とします。その場合、以下の State 1, 2ORで接続します。

  • State 1
    • Negate statement results (NOT)
    • Inspect: URI Path/hoge を設定
  • State 2
    • Negate statement results (NOT)
    • Inspect: Bodyfuga-user を設定

複数条件の適用に際しては、他にラベルを用いた方法も一般的のようです。

この機能を使い始めて改めて感じたことですが、誤検知対応とは言え、除外設定を追加するという行為自体が「本来のルールの効果を低減させる」ことは間違いなく、安易に除外することは避けるべきですね。可能な限り条件を組み合わせ、本来のアクセスのみを許可するよう絞ることが、理想だと思います。また簡単なStatementであったとしても、十分な検証が必須です。

さらに踏み込んだ除外設定

上記でも解決しない場合、またはWAF運用に割けるリソースが限られている場合、ルールグループ全体の適用を諦めるのではなく、ルールを個別にCOUNTにすることも可能です。

具体的な例ですと、ルールグループAWSManagedRulesSQLiRuleSetには、以下のようなルールが含まれています。これらを個別にCOUNT / BLOCKすることが可能となっています。

  • SQLi_QUERYARGUMENTS
  • SQLi_BODY
  • SQLi_COOKIE
  • ...

これらのうち、例えば SQLi_COOKIE だけをOverride to Countとし、残りの項目をBLOCKモードで稼働させるといったことができます。

これも除外範囲を広げるという意味で決して推奨はされないはずですが、アプリケーションやルールの特性によっては効率的な場合があるかもしれませんので、その場合には検討できそうです。

おまけ: 設定項目を調べる方法

最近、インフラ管理にTerraformを使うことが多いのですが、Terraformのドキュメントを読んでいると、設定項目にそのマネージドサービスでできること(=選択肢)が網羅されていることに気づきます。そしてその部分を読むだけで、そのサービスの解像度があがる感覚を持てると思います。

もちろんWAFに限った話では無いですが、もしTerraformを導入済みであればTerraformのAWS プロバイダーのドキュメントで、普段CDKを活用しているのであればCDKのドキュメントで、関連リソース(AWS WAFならWeb ACL)のページを早い段階で読むと効率的かもしれません。

https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/wafv2_web_acl

https://docs.aws.amazon.com/cdk/api/v1/docs/@aws-cdk_aws-waf.CfnWebACL.html

おわりに

やや断片的になりましたが、導入時に重要となるトピックをご紹介しました。
多少の運用上の工夫や仕様の理解は必要ですが、これだけ機能が豊富で、廉価に導入できるAWS WAF最強では? という気持ちになっています。

今やWAFはクラウド上のワークロードを運用する上で必須になりつつあると思いますが、是非とも様々なルールを積極的に使っていきたいと思います。(もちろんサードパーティのWAFもメリットが多くあり、併用するとさらに良さそうという記事も書きました。)

本記事が、これからWAF導入を検討される方の参考になれば幸いです。

Discussion