はじめに

平素は大変お世話になっております。
クイックガードのパー子です。

AWS WAF を運用する際、AWSマネージドなルールを活用しつつ、カスタムルールで細部の挙動を調整することが多いかと思います。

ここで気になるのがカスタムルールの WCU (Web ACL Capacity Unit) の値です。
マネージドなルールはドキュメントに WCU値が明記されていますが、カスタムルールは作ってみるまで正確な数値がわかりません。

そこで今回は、公式ドキュメントの記載と、実際に AWSコンソール上でルールを作成して検証した結果をもとに、カスタムルールの WCU の目安をまとめてみました。

国/地域

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-geo-match.html

リクエスト元の国および地域を検査します。

国/地域の個数によらず、WCU は 1 です。

基本値WCU
国/地域リスト1

リクエスト元の判定方法として以下の 2種類から選択できますが、どちらを選んでも WCU は変わりません。

  • 送信元IPアドレス
  • 任意のリクエストヘッダー (X-Forwarded-For など) 中の IPアドレス
オプションWCU
送信元アドレス+0
リクエストヘッダー中のアドレス+0

IPアドレス

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-ipset-match.html

リクエスト元の IPアドレスを検査します。

最大で 10,000個 の IPアドレスを紐付けられますが、どれだけ紐付けても WCU は 1 でした。

基本値WCU
IPアドレスリスト1

リクエスト元の判定方法は “国/地域” と同じ 2種類ですが、任意のヘッダーの場合、どの位置の IPアドレスを検査対象とするか指定できます。

  • First
  • Last
  • Any

First または Last の場合には WCU は変わりませんが、Any の場合は +4 されます。

オプションWCU
First+0
Last+0
Any+4

ASN

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-asn-match.html

リクエスト元の IPアドレスが属する ASN (Autonomous System Number) を検査します。

ASN の個数によらず WCU は 1 です。

基本値WCU
ASNリスト1

ラベル

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-label-match.html

評価チェーンの前方にあるルールによって付与されたラベルを検査します。
目的のラベルが付与されているかどうか、単一の値を指定できます。

WCU は 1 です。

基本値WCU
ラベル1

評価スコープとして以下の 2種類がありますが、WCU は変わりません。

  • Label: ラベル完全一致
  • Namespace: 名前空間 (:区切り)
オプションWCU
Label+0
Namespace+0

リクエスト構成要素

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-fields-list.html

リクエストヘッダーやボディなど、HTTPリクエストの構成要素に対して検査できます。

構成要素やオプションによって WCU が変動します。

構成要素ごとの基本値

構成要素の WCU は基本的に 0 であり、評価方式 (文字列一致やサイズなど) によって WCU が加算される… と考えるとわかりやすいです。

ただし、一部の構成要素には WCU が課されます。

基本値WCU
HTTP メソッド0
単一ヘッダー0
すべてのヘッダー0
ヘッダーの順序0
Cookie0
URI フラグメント0
URI パス0
JA3 フィンガープリント0
JA4 フィンガープリント0
クエリ文字列0
単一クエリパラメータ10
すべてのクエリパラメータ10
本文0
JSON 本文x2

単一クエリパラメータ:

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-fields-list.html#waf-rule-statement-request-component-single-query-param

クエリ文字列をパラメータごとにパースした、その特定の 1つのパラメータを検査します。

ドキュメントには記載がありませんが、WCU は 10 です。

すべてのクエリパラメータ:

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-fields-list.html#waf-rule-statement-request-component-all-query-params

特定のパラメータではなく、すべてのパラメータの値を対象に検査します。

ドキュメントに記載のとおり WCU は 10 です。

JSON 本文:

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-fields-list.html#waf-rule-statement-request-component-json-body

リクエストボディを JSON としてパースしたものを検査します。

これ自体の WCU は 0 なのですが、ステートメント本来の合計WCU が 2倍されます。

2倍となる範囲は単一のステートメントのみで、AND や OR で連結している他のステートメントには影響しません。

構成要素ごとのオプション

例えば “すべてのヘッダー” では、マッチングのスコープ (= ヘッダーのキーを見るのか? 値を見るのか?) や オーバーサイズ時の振る舞い (= 一致したと見なすか? そうでないか?) などを指定できますが、いずれも WCU に変化はありません。

オプションWCU
(構成要素により異なるが、いずれも)+0

一致タイプ

評価方式として、文字列の一致やサイズの大小など、いくつかのタイプに分類できます。

各タイプの WCU は以下のように設定されています。

文字列

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-string-match.html

与えられた値を文字列として評価します。

基本値WCU
完全一致2
○○で始まる2
○○で終わる2
○○を含む10
単語を含む10
正規表現3
正規表現パターンセット25

単語を含む:

/[A-Za-z0-9_]+/ が単語として扱われます。
“○○を含む” と違い、行頭行末を含むこれ以外の文字が Delimiter として前後に存在する場合のみ検知されます。

正規表現:

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-regex-match.html

正規表現パターンの長短や複雑さによらず、WCU は 3 となるようです。

“○○を含む” が 10 なので、WCU を少しでも抑えたい場合は正規表現を使うとよいかもしれません。

正規表現パターンセット:

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-regex-pattern-set-match.html

複数のルールで使い回しができるように、任意の個数の正規表現パターンをセットにして名前をつけたものを正規表現パターンセットと呼びます。

セット中のパターン数によらず、WCU は 25 です。

サイズ

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-size-constraint-match.html

文字列のバイト数を評価します。

いずれも WCU は 1 です。

基本値WCU
等しい1
等しくない1
以下1
未満1
以上1
大きい1

攻撃

サイバー攻撃のシグネチャを検査します。

高負荷な処理となっているようで、WCU は大きめです。

基本値WCU
SQLインジェクション (Low)20
SQLインジェクション (High)30
XSS40

SQLインジェクション:

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-sqli-match.html

検査の感度によって WCU が異なります。

Low は 20、High は 30 です。

XSS:

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-xss-match.html

WCU は最も大きく、40 です。

テキスト変換

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-transformation.html

対象を評価する際に、下処理として値を変換 (= 小文字化や各種デコードなどのプリセットされた変換関数を適用) することができます。

複数個の変換関数を適用できて、1つの変換ごとに WCU が 10 追加されます。

オプションWCU
変換関数 (1つあたり)+10

補足

テキスト変換は “JSON 本文” による WCU 2倍の範囲外となっています。
つまり、2倍されたあとに 10 が加算されます。

レート

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statement-type-rate-based.html

HTTPリクエストの流入数に基づいてレートを制限します。

閾値や評価期間によらず、WCU は 2 です。

基本値WCU
レート2

リクエスト数をカウントする際の 集約キー を以下から選択できます。

  • 送信元IPアドレス
  • 任意のリクエストヘッダー中の IPアドレス
  • すべて
  • リクエスト構成要素やラベル、ASN に基づくカスタムキー

カスタムキーは任意の個数を設定できて、1つごとに WCU 30 が課されます。
各キーにはテキスト変換を適用することもできますが、リクエスト構成要素の検査の場合とは異なり、WCU は加算されません。

オプションWCU
送信元アドレス+0
リクエストヘッダー中のアドレス+0
すべて+0
カスタムキー 1つあたり+30
 ┗ テキスト変換+0

また、スコープダウン・ステートメント を用いて、カウント対象とするリクエストをフィルタすることができます。

スコープダウン・ステートメントにはこれまで述べてきた任意のステートメントを組み合わせて使用できます。
WCU は内包する構成ステートメントの総和となります。

オプションWCU
スコープダウンなし (すべてカウント)+0
スコープダウンあり+(構成ステートメントの総和)

補足

集約キーを “すべて” とした場合は、スコープダウン・ステートメントの使用が必須となります。

ルール共通

続いて、ステートメントによらないルールレベルでのオプションを見ていきます。

アクション

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-action.html

ルール条件にマッチした際の WAF の処理内容を指定します。

オプションWCU
Allow+0
Block+0
Count+0
CAPTCHA+0
Challenge+0

いずれも WCU は加算されませんが、CAPTCHA と Challenge には 追加料金 がかかります。

論理ステートメント

https://docs.aws.amazon.com/waf/latest/developerguide/waf-rule-statements-logical.html

複数のステートメントを連結するための論理演算子です。

これ自体の WCU は 0 です。

基本値WCU
AND0
OR0
NOT0

カスタムリクエスト/レスポンス

https://docs.aws.amazon.com/waf/latest/developerguide/waf-custom-request-response.html

ルール条件にマッチした際、リクエストやレスポンスをカスタマイズできます。

いずれも WCU はかかりません。

オプションWCU
リクエスト+0
レスポンス+0

ラベル付与

https://docs.aws.amazon.com/waf/latest/developerguide/waf-labels.html

ルール条件にマッチした際に、メタデータとしてラベルを付与できます。
付与されたラベルは後続のルールにおいて参照できます。

WCU はラベル 5つごとに 1 加算されます。

オプションWCU
ラベル (5つあたり)+1

最適化

基本的に Protection pack (Web ACL) の総コストは各ルールの WCU の総和となりますが、ルールの組み合わせ次第では最適化が働いて WCU が割り引かれることがあります。

例えば以下のように、小文字に変換した URIパスに対して文字列の完全一致でチェックするルールを 2つ用意します。

各ルールの WCU は 文字列完全一致 (2) + テキスト変換 (10) = 12 となり、本来であればそれが 2つ分で Protection pack の総コストは 24 となるはずなのですが、

実際の結果は以下のとおり 14 となります。

以下のドキュメントに記載されているように、2つのルールで共通する “URIパスに対する小文字への変換処理” が最適化されて、テキスト変換のコスト計上が 1回分に抑えられたのでしょう。

https://docs.aws.amazon.com/waf/latest/developerguide/aws-waf-capacity-units.html

For example, if you define two rules to examine the same web request component, and the rules each apply a particular transformation to the component before inspecting it, AWS WAF might be able to charge you just once for applying the transformation.

WCU計算例

最後におさらいとして複雑なルールを組んでみて、その要素ごとの WCU を計算してみます。

ルールが長く、UI上では見づらいため、JSON にコメントを入れる形で記載します。
[#n] でコメントした部分が実際に加算される WCU です。

{
  "Name": "my-rule",
  "Priority": 0,
  "Statement": {
    "RateBasedStatement": {    // [#1] レートベースのチェック (WCU=2)
      "Limit": 4649,
      "AggregateKeyType": "CUSTOM_KEYS",    // カスタムキーによる集約 (キーごとに +30)
      "EvaluationWindowSec": 120,
      "ScopeDownStatement": {    // スコープダウン・ステートメント (内包するステートメントの総和が加算される)
        "AndStatement": {    // 論理ステートメントはコスト0
          "Statements": [
            {
              "NotStatement": {    // 論理ステートメントはコスト0
                "Statement": {
                  "IPSetReferenceStatement": {    // [#2] IPアドレスのチェック (WCU=1)
                    "ARN": "arn:aws:wafv2:us-east-1:123456789012:global/ipset/waf-test_ip_IPV4/123e4567-e89b-12d3-a456-426614174000",
                    "IPSetForwardedIPConfig": {
                      "HeaderName": "X-Forwarded-For",
                      "FallbackBehavior": "MATCH",
                      "Position": "ANY"    // [#3] ヘッダー中の任意の位置 (WCU=4)
                    }
                  }
                }
              }
            },
            {
              "RegexMatchStatement": {    // [#4] 正規表現による文字列一致 (WCU=3)
                "RegexString": "qqq",
                "FieldToMatch": {
                  "SingleQueryArgument": {    // [#5] 単一クエリパラメータ (WCU=10)
                    "Name": "p"
                  }
                },
                "TextTransformations": [
                  {
                    "Priority": 0,
                    "Type": "NONE"
                  }
                ]
              }
            },
            {
              "SqliMatchStatement": {    // [#6] SQLインジェクション (感度High) のチェック (#7 の効果により本来の 2倍; WCU=60)
                "FieldToMatch": {
                  "JsonBody": {    // [#7] JSON本文 (WCU を 2倍)
                    "MatchPattern": {
                      "IncludedPaths": ["/a/b/c"]
                    },
                    "MatchScope": "ALL",
                    "OversizeHandling": "CONTINUE"
                  }
                },
                "TextTransformations": [    // [#8] テキスト変換 2つ (WCU=20)
                  {
                    "Priority": 0,
                    "Type": "COMPRESS_WHITE_SPACE"
                  },
                  {
                    "Priority": 1,
                    "Type": "HTML_ENTITY_DECODE"
                  }
                ],
                "SensitivityLevel": "HIGH"
              }
            }
          ]
        }
      },
      "CustomKeys": [    // [#9] 集約カスタムキー 2つ (WCU=60)
        {
          "Header": {
            "Name": "h",
            "TextTransformations": [    // カスタムキーの場合はテキスト変換のコスト0
              {
                "Priority": 0,
                "Type": "LOWERCASE"
              },
              {
                "Priority": 1,
                "Type": "COMPRESS_WHITE_SPACE"
              }
            ]
          }
        },
        {
          "UriPath": {
            "TextTransformations": [
              {
                "Priority": 0,
                "Type": "URL_DECODE"
              },
              {
                "Priority": 1,
                "Type": "NORMALIZE_PATH"
              },
              {
                "Priority": 2,
                "Type": "MD5"
              }
            ]
          }
        }
      ]
    }
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "my-rule"
  },
  "Action": {
    "Block": {
      "CustomResponse": {    // カスタムレスポンスはコスト0
        "ResponseCode": 403,
        "CustomResponseBodyKey": "waf-test-response",
        "ResponseHeaders": [
          {
            "Name": "k",
            "Value": "v"
          }
        ]
      }
    }
  },
  "RuleLabels": [    // [#10] ラベル付与 5つ組 2セット (WCU=2)
    {
      "Name": "l1"
    },
    {
      "Name": "l2"
    },
    {
      "Name": "l3"
    },
    {
      "Name": "l4"
    },
    {
      "Name": "l5"
    },
    {
      "Name": "l6"
    }
  ]
}

#1 から #10 までで、合計 162 WCU です。

まとめ

以上、AWS WAF のカスタムルールにおける WCU値について、公式ドキュメントと検証をもとにステートメントの種類やルールのオプションごとの加算条件を整理しました。

主なポイントは以下のとおりです。

  • 国/地域、IPアドレス、ASN、ラベルのチェックは基本的に WCU=1 と低コスト
  • リクエスト構成要素のチェックは一致タイプによって変動し、文字列の完全一致で WCU=2、正規表現で WCU=3、“含む"検索で WCU=10、正規表現パターンセットで WCU=25
  • 攻撃検知は高コストで、SQLインジェクションの感度Low が WCU=20、同High が WCU=30、XSS が WCU=40
  • 単一/すべてのクエリパラメータは WCU=10
  • JSON 本文は他の WCU を 2倍にする
  • テキスト変換は 1つあたり WCU=10
  • レートベースルールは基本 WCU=2 だが、カスタムキーは 1つあたり WCU=30 と高コスト
  • 論理ステートメント、アクション、カスタムリクエスト/レスポンスは WCU=0
  • ラベル付与は 5つあたり WCU=1
  • 共通処理の最適化により、単純な総和よりも WCU が割り引かれることがある

カスタムルール作成前の見積もりの参考にしていただければ幸いです。

今後ともよろしくお願い申し上げます。