【目次】

  1. 1. はじめに
  2. 2. ユースケース1: ユーザーを個別に識別できるIDを使用して制限する
  3. 3. ユースケース2: 特定のページに大量アクセスがあった場合、ボットでなければ許可する
  4. 4. ユースケース3: 特定のページへ大量のアクセスがあった場合にブロックする
  5. 5. ユースケース4: 特定のヘッダーの値が空になっているなどの特徴を元に制限する
  6. 6. おわりに

1. はじめに

2023年5月16日にレートベースルールの条件をより詳細にできるようになったとAWSから発表がありました。
AWS WAF enhances rate-based rules to support request headers and composite keys

これまでレートベースルールでは発生元 IP アドレスからのリクエストのレートのみ追跡可能でした。
スコープダウンステートメントを使用することで、レートベースルールの対象とする範囲をURIなどで絞ることはできたものの、IPアドレスが異なる場合などは多数のリクエストがあってもブロックできない場合がありました。

今回のリリースにより、今後はリクエストに含まれるIPアドレス以外の条件でもレートを追跡できるようになります。
本ブログでは、レートベースルールのユースケースをいくつかご紹介します。

レートベースルールについては、以下のブログ記事をご参考ください。
レートベースルールの使い方

2. ユースケース1: ユーザーを個別に識別できるIDを使用して制限する

今回のリリースで追加された追跡項目の中に、「Custom Key」というものがあります。
こちらを使うことで、ヘッダーやクエリなどに含まれる値を指定することができます。

ユーザーを個別に識別できるIDがヘッダー内に存在する場合には、以下のような形で指定することで同じIDがヘッダーに含まれる複数IPアドレスからのリクエストをブロックすることが可能です。
1ユーザーに対して同じのIDが必ず振られる仕組みとなっていて、特定のユーザーの大量アクセスを止めたいといった場合には有効です。

{
  "Name": "ratebased-id",
  "Priority": 0,
  "Statement": {
    "RateBasedStatement": {
      "Limit": 100,
      "AggregateKeyType": "CUSTOM_KEYS",
      "CustomKeys": [
        {
          "Header": {
            "Name": "ID",
            "TextTransformations": [
              {
                "Priority": 0,
                "Type": "NONE"
              }
            ]
          }
        }
      ]
    }
  },
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "ratebased-id"
  }
}

上記の通り、追跡する条件はヘッダーに含まれるIDのみとなりますので、IPアドレスが変わった場合でも条件にマッチします。

もし、同じIDかつ同じIPアドレスで追跡したい場合には、2つの目キー「Request aggregation key 2」としてIPアドレスも指定できます。
この場合、IDが同じであってもIPアドレスが変わった際にはルールにマッチしません。

なお、1つ目のキーを指定する際にも表示されますが、IPアドレスは2つ目以降のキーの場合にのみ指定が可能ですので、ご注意ください。

3. ユースケース2: 特定のページに大量アクセスがあった場合、ボットでなければ許可する

ボットからの大量アクセスが特定のURIに対して発生するような場合も、レートベースルールで制限可能です。

今回追加された「Count all」というオプションでは、スコープダウンステートメントを使用して、指定した条件に一致するリクエストすべてを追跡対象にできます。

例えば、お問い合わせフォームに対してボットから大量のアクセスがあった場合、アクションをCAPTCHAにすることで、ボットは制限しつつ、一般的なユーザーについてはお問い合わせフォームへのアクセスを許可できます。

「/contact」以下のページに対して、5分間に100回以上アクセスしたらCAPTCHAを表示したい場合には、以下のようなルールを作成できます。

{
  "Name": "ratebased-uri-captcha",
  "Priority": 0,
  "Statement": {
    "RateBasedStatement": {
      "Limit": 100,
      "AggregateKeyType": "CONSTANT",
      "ScopeDownStatement": {
        "ByteMatchStatement": {
          "SearchString": "/contact",
          "FieldToMatch": {
            "UriPath": {}
          },
          "TextTransformations": [
            {
              "Priority": 0,
              "Type": "NONE"
            }
          ],
          "PositionalConstraint": "STARTS_WITH"
        }
      }
    }
  },
  "Action": {
    "Captcha": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "ratebased-uri-captcha"
  },
  "CaptchaConfig": {
    "ImmunityTimeProperty": {
      "ImmunityTime": 120
    }
  }
}

このルールの追跡対象は指定した条件にマッチするすべてのリクエストなので、今回の場合は「/count」以下のページに対しては一度閾値を超えると、レートベースルールの有効時間が過ぎるまではどのIPアドレスからのリクエストであってもCAPTCHAが表示されます。
CAPTCHA認証をクリアすると、ルール上で設定したImmunity timeの間は何度リクエストを送信しても許可されます。

また、「Count all」オプションを使用した場合、ユースケース1の「Custom Key」のようにIPアドレスを追跡対象に含めることはできません。

レートベースルールとCAPTCHAアクションの組み合わせについては、以下のブログ記事でもご紹介しておりますので、あわせてご参考ください。
AWS WAF CAPTCHA を利用してボットトラフィックから保護する

4. ユースケース3: 特定のページへ大量のアクセスがあった場合にブロックする

上記で紹介した「Count all」オプションを使って、アクションにブロックを選択することで大量のアクセスをとりあえずブロックするということも可能です。
このURIについては大量アクセスがあったらすぐにブロックしたいといったケースには有効ではないでしょうか。

この場合、ユースケース2と同じような形で、アクションをBLOCKに変更すればルールの作成が可能です。

ただし、CAPCTHAアクションとは異なり、レートベースルールの有効時間が過ぎるまではどのIPアドレスからでも対象のURIにはアクセスできない状況となりますので、意図せずサービスを止めてしまうといったことがないよう、指定する条件については事前の検討が必要かと思います。

まずはCOUNTで適用し、どういったリクエストがマッチするか様子を見てからBLOCKへ変更するといった対応も可能です。

5. ユースケース4: 特定のヘッダーの値が空になっているなどの特徴を元に制限する

これまで弊社で観測したことがあるDDoS攻撃のような大量のリクエストを検知するルールを考えてみます。
複数のIPからの大量のリクエストで、IPアドレスも変えている兆候がありました。
いくつかのアクセスをピックアップして同様の特徴がないか確認すると User-Agent が空であったという点が共通していました。

このような特徴を発見し、レートベースルールを作成するという手段を検討してみます。
※ User-Agent ヘッダーが存在しない時点で単純に BLOCK でも対策としては可能です。

特定のUser-Agentからのアクセスが大量にあった場合は、以下のようなルールで対象の条件を追跡できます。
ユースケース1の通り、「Custom Key」を使用した場合はIPアドレスをキーとして指定するかどうかで挙動が変わる点に注意が必要です。

{
  "Name": "ratebased-UA-value",
  "Priority": 1,
  "Statement": {
    "RateBasedStatement": {
      "Limit": 100,
      "AggregateKeyType": "CUSTOM_KEYS",
      "CustomKeys": [
        {
          "Header": {
            "Name": "User-Agent",
            "TextTransformations": [
              {
                "Priority": 0,
                "Type": "NONE"
              }
            ]
          }
        }
      ]
    }
  },
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "ratebased-UA-value"
  }
}

User-Agentヘッダー自体が存在しないリクエストの場合は、「Count all」オプションでスコープダウンステートメントのNOTステートメントを使用します。

{
  "Name": "ratebased-no-UA",
  "Priority": 0,
  "Statement": {
    "RateBasedStatement": {
      "Limit": 100,
      "AggregateKeyType": "CONSTANT",
      "ScopeDownStatement": {
        "NotStatement": {
          "Statement": {
            "SizeConstraintStatement": {
              "FieldToMatch": {
                "SingleHeader": {
                  "Name": "user-agent"
                }
              },
              "ComparisonOperator": "GT",
              "Size": 0,
              "TextTransformations": [
                {
                  "Priority": 0,
                  "Type": "NONE"
                }
              ]
            }
          }
        }
      }
    }
  },
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "ratebased-no-UA"
  }
}

6. おわりに

レートベースルールで追跡できる項目を詳細に指定できるようになったことで、様々なユースケースにも対応できるようになりました。
DDoSへの対策とは言い切れないものの、複数のIPアドレスからの大量アクセスであっても条件さえ一致すればブロックできるという意味では緩和策としてさらに期待できるかと思います。

その反面、条件が複雑化しルール作成の難易度があがりました。
条件によっては意図せずアクセスをブロックしてしまう可能性もありますので、詳細な条件のレートベースルールを作成する際には、あらかじめ検証を行ってからアクションを変更することをおすすめします。