naoketa.net

AWS WAFをマネージルール単位で制御してWAF誤遮断に対処する

January 29, 2023

はじめに

AWS WAFのマネージドルールを使ってサイトを運営していると、思わぬ箇所でWAFによる誤遮断が発生することがあります。こうした誤遮断に対処するため、例えば特定のIPやサイトのURLパスのみ遮断対象から除外する対応を行うケースがあります。

単純に特定のパスでAWS WAFマネージドルールのルールグループをまるごと除外する場合は、スコープダウンステートメントを使って除外することが可能ですが、個別のルールごとに対応を行いたい場合、こうした対応ができません。

今回は個別のマネージドルール単位x特定条件(パス単位など)で除外するケースを紹介します。

今回のユースケース

今回のお題となるユースケースでは、Webアプリケーションで一般的なWAFルールが設定されれいるコアルールセットのマネージドルールグループを適用したいサイトを想定します。ただこのWebアプリケーションでは一部の導線のみクエリ文字列が2,048バイトを超えるようなリクエストが通常の機能として想定されています。

クエリ文字列が2,048バイトを超えるようなリクエストは、前述のコアルールセットのマネージドルールグループに含まれるSizeRestrictions_QUERYSTRINGのルールアクションにより遮断されてしまいます。

前述の一部のパスに絞ってクエリ文字列が2,048バイトを超えるようなリクエストを許容する。かつ他の動線では遮断する方法について考えます。

$ curl "https://example.com/index.html?{URL文字列の合計2,048バイトになるクエリ}"
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<HTML><HEAD><META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
<TITLE>ERROR: The request could not be satisfied</TITLE>
</HEAD><BODY>
<H1>403 ERROR</H1>
<H2>The request could not be satisfied.</H2>
<HR noshade size="1px">
Request blocked.
We can't connect to the server for this app or website at this time. There might be too much traffic or a configuration error. Try again later, or contact the app or website owner.
<BR clear="all">
If you provide content to customers through CloudFront, you can find steps to troubleshoot and help prevent this error by reviewing the CloudFront documentation.
<BR clear="all">
<HR noshade size="1px">
<PRE>
Generated by cloudfront (CloudFront)
Request ID: xxxxx==
</PRE>
<ADDRESS>
</ADDRESS>
</BODY></HTML>%

今回のシステム構成

今回は簡略化のため、CloudFront経由でS3上の静的コンテンツを配信するシンプルなユースケースにおいて、CloudFrontにアタッチするAWS WAFマネージドルールを配信されるコンテンツごとに制御を変えるケースを紹介します。 AWS WAFのアタッチ先がALBでも基本的な方法は同じです。

システム構成

CloudFrontで配信されるURLのうち、/page1は許可したい。/page2は遮断したいというケースになります。

example.com/
 ├ page1/     //`SizeRestrictions_QUERYSTRING`のルールを**許可**したい
 ├ page2/     //`SizeRestrictions_QUERYSTRING`のルールを**遮断**したい

一部のパスだけでマネージドルールグループすべてを除外したい

このような場合、SizeRestrictions_QUERYSTRINGのルールに限らずコアルールセットのマネージドルールグループすべてを一部のパスだけで除外する場合は、スコープダウンステートメントにより制御することができます。

WebACLのルールは以下のようなイメージになります。

{
  "Name": "AWSManagedRulesCommonRuleSet",
  "Priority": 10,
  "Statement": {
    "ManagedRuleGroupStatement": {
      "VendorName": "AWS",
      "Name": "AWSManagedRulesCommonRuleSet",
      "ScopeDownStatement": {
        "NotStatement": {
          "Statement": {
            "ByteMatchStatement": {
              "SearchString": "/page1/",
              "FieldToMatch": {
                "UriPath": {}
              },
              "TextTransformations": [
                {
                  "Priority": 0,
                  "Type": "NONE"
                }
              ],
              "PositionalConstraint": "EXACTLY"
            }
          }
        }
      }
    }
  },
  "OverrideAction": {
    "None": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": false,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "AWSManagedRulesCommonRuleSetMetric"
  }
}

一部のパスだけでマネージドルールグループの一部のルールのみ除外したい

上記の方法の場合、SizeRestrictions_QUERYSTRINGのルール以外のマネージドルールグループ全てが対象導線で無効になってしまいます。SizeRestrictions_QUERYSTRINGのルールのみで、一部のパスで許可する場合は、ラベルを使って複数のルールを組み合わせたWebACLを作成することで実現することができます。

具体的には優先度の高い初めにチェックされるルールでは、コアルールセットのマネージドグループを設定して、SizeRestrictions_QUERYSTRINGのルールのみCOUNT、その他のルールはBLOCKにします。

そして後続のルールでは特定のパスを対象にチェックするルールを作成して、前述のルールでCOUNTされたSizeRestrictions_QUERYSTRINGがラベル設定されているリクエストのみブロックするルールを設定します。こうすることで、一部のパスだけでマネージドルールグループの一部のルールのみ除外するという要件を満たすことができます。

こちらもWebACLの設定のイメージは以下のようになります。

  • コアルールセットのマネージドグループを設定して、SizeRestrictions_QUERYSTRINGのみCOUNTするルール
{
  "Name": "AWSManagedRulesCommonRuleSet",
  "Priority": 10,
  "Statement": {
    "ManagedRuleGroupStatement": {
      "VendorName": "AWS",
      "Name": "AWSManagedRulesCommonRuleSet",
      "RuleActionOverrides": [
        {
          "Name": "SizeRestrictions_QUERYSTRING",
          "ActionToUse": {
            "Count": {}
          }
        }
      ]
    }
  },
  "OverrideAction": {
    "None": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "AWSManagedRulesCommonRuleSetMetric"
  }
}
  • 上記のルールでSizeRestrictions_QUERYSTRINGにラベル設定されたリクエストのうち、該当パスでのみブロックするルール
{
  "Name": "Custom-SizeRestrictions_QUERYSTRING",
  "Priority": 20,
  "Statement": {
    "AndStatement": {
      "Statements": [
        {
          "LabelMatchStatement": {
            "Scope": "LABEL",
            "Key": "awswaf:managed:aws:core-rule-set:SizeRestrictions_QUERYSTRING"
          }
        },
        {
          "NotStatement": {
            "Statement": {
              "ByteMatchStatement": {
                "SearchString": "/page1/",
                "FieldToMatch": {
                  "UriPath": {}
                },
                "TextTransformations": [
                  {
                    "Priority": 0,
                    "Type": "NONE"
                  }
                ],
                "PositionalConstraint": "STARTS_WITH"
              }
            }
          }
        },
    }
  },
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "Custom-SizeRestrictions_QUERYSTRING"
  }
}

さいごに

今回はAWS WAFマネージドグループのうち、個別のルールで細かく制御する方法について記載しました。Webアプリケーションの意図したリクエストについてWAFの誤遮断が発生すると、マネージドルールについてはWAFルールの制御ができないと思い広く開けてしまうケースも多いかと思いますが、できるだけ細かく上記のように対応できるものは細かく設定できるようにしておきたいです。


© 2024, Built with Gatsby by Naoki Tazawa