AWS WAF 動的ラベル補間(Dynamic Label Interpolation)の解説とヘッダー・レスポンスへの動的展開を確認してみた

記事タイトルとURLをコピーする

こんにちは、近藤(りょう)です!

2026年5月に AWS WAF の 動的ラベル補間(Dynamic Label Interpolation) が追加されました。WAF のラベル機能自体は以前からありましたが、「ラベルの値そのものをヘッダーやレスポンスボディに書き出す」ことはできませんでした。

今回追加された 動的ラベル補間(Dynamic Label Interpolation)により、ラベル値を動的にヘッダーやレスポンスへ埋め込めるようになりました。

本記事では機能の仕組みと AWS が公開している CDK サンプルを使った実動作の内容を紹介します。

aws.amazon.com

対象読者: AWS WAF を運用中、またはこれから導入を検討しているエンジニア

AWS WAF のラベルとは?

AWS WAF は各ルールの評価結果に ラベル を付与できます。ラベルはリクエストに「タグ」を貼るようなイメージで後続のルールがそのタグを参照して追加の判定を行えます。

1. Bot Control が評価
2. awswaf:managed:aws:bot-control:bot:category:scraping ラベルを付与
3. 後続ルール:そのラベルにマッチしたら Block / Count など

ラベルは 名前空間:値 の構造になっています。

awswaf:managed:aws:bot-control:bot:category:scraping
└─────────────────────────────────────────┘ └──────┘
               名前空間(namespace)            値

例えば、Bot Control を使うとボットの分類結果(scraping / search_engine / http_library など)がラベルとして付与されます。このラベルを使えば「スクレイパーはブロック」「検索エンジンは通過」といった制御が1つのルールで書けます。

docs.aws.amazon.com

動的ラベル補間(Dynamic Label Interpolation)とは?

AWS WAF のルール評価時に付与されたラベルの値を ${namespace:} 構文で動的にヘッダーやレスポンスへ埋め込める機能です。今までは、ラベルは WAF のルール評価中にしか使えずオリジン(ALB / Lambda 等)にはラベルの値が届きませんでした。

例えば、Bot Control や独自ルールで付与されたラベル値をオリジンへ渡したい場合、ラベル値ごとに個別ルールと静的ヘッダーを定義する必要がありました。

動的ラベル補間では ${namespace:} 構文を利用することで、ラベル名前空間全体を動的に展開できるようになりました。これにより、ルール数の削減・運用負荷の軽減・柔軟なアプリケーション連携がうれしいポイントかもしれません。

また、クライアント IP・WAF リクエスト ID・JA3/JA4 フィンガープリントといったリクエストコンテキストから生成される合成ラベルも利用できます。

docs.aws.amazon.com

構文の詳細(マネージドラベル/カスタムラベル/合成ラベル)

■ マネージドラベル
Bot Control や ATP が付与するラベルについては、以下のドキュメントを参照してください。

■ カスタムラベル
自分で付与したカスタムラベルを補間する場合は、アカウント ID と WebACL 名のプレフィックスが必要です(ラベルの構文と命名要件)。

${awswaf:<ACCOUNT_ID>:webacl:<WEBACL_NAME>:<カスタム名前空間>:}

※マネージドラベルと構文が異なる点に注意が必要です。

■ 合成ラベル
合成ラベルは、ラベルではなくリクエストコンテキストから生成される特殊な値です。(動的ラベル補間ドキュメント

構文
${awswaf:ip:} クライアント IP アドレス
${awswaf:request_id:} WAF リクエスト ID
${awswaf:ja3:} JA3 TLS フィンガープリント
${awswaf:ja4:} JA4 TLS フィンガープリント

使える箇所

場所 アクション 用途
カスタムリクエストヘッダー Count の customRequestHandling ラベル値をオリジンに転送
カスタムレスポンスヘッダー Block の customResponse 動的リダイレクト URL の生成など
カスタムレスポンスボディ Block の customResponse ブロックページへの情報埋め込み

Dynamic label interpolation - AWS WAF, AWS Firewall Manager, AWS Shield Advanced, and AWS Shield network security director

動的ラベル補間機能の動作確認(全6パターン)

AWS が公開している CDK サンプルがあります。

github.com

このサンプルは CloudFront + WAF WebACL(Bot Control V5 Targeted)+ Lambda Function URL で構成で動的ラベル補間のパターンを一通り確認できます。

CloudFront → WAF WebACL → Lambda Function URL 構成

デプロイ

デプロイ手順は GitHub の Deploy セクション を参照してください。

ルール構成

Priority ルール名 デモ内容
50 challenge-root / に Challenge を適用して WAF トークンを発行(フィンガープリント・トークン ID の取得を有効化)
100 bot-control Bot Control V5 Targeted を Count モードで適用し、後続ルールが参照するラベルを生成
150 classify-tier カスタムラベルのデモ:x-api-key ヘッダーを検査し、pk_enterprise_* に一致すれば app:tier:enterprise ラベルを付与
160 forward-tier カスタムラベルの補間:${app:tier:}x-customer-tier リクエストヘッダーに展開してオリジンへ転送
300 block-with-custom-page レスポンスボディの補間:403 ブロックページに ${awswaf:ip:}${awswaf:request_id:} を埋め込み
400 verify-redirect レスポンスヘッダーの補間:302 リダイレクトの Location ヘッダーに ${awswaf:ip:}${awswaf:request_id:} を展開
500 app-signalling リクエストヘッダーの補間:bot:category / bot:name / signal / fingerprint / token ID / client IP の 6 名前空間を x-waf-* ヘッダーとしてオリジンへ転送
700 cache-segmentation リクエストヘッダー補間 + CloudFront キャッシュキー:${awswaf:managed:aws:bot-control:bot:category:} でボットカテゴリ別にキャッシュを分離

本記事で動作確認した時点のルール構成です。最新のルールは GitHub の README - Demo use cases を参照してください。

(参考)ルール一覧

ルールの評価

今回のルールの評価フローは、前半でラベル付与・WAF トークン発行を行い後半で補間して各出力先に展開する構成になっています。

リクエストのパスによって WAF がその場でレスポンスを返すかヘッダーを挿入してオリジンに転送するかが変わります。

sequenceDiagram
    actor Client as クライアント
    participant CF as CloudFront
    participant WAF as WAF WebACL
    participant Lambda as Lambda<br/>Function URL

    Client->>CF: HTTPS リクエスト
    CF->>WAF: ルール評価開始

    Note over WAF: P50: challenge-root
    WAF-->>Client: JavaScript Challenge(WAF トークンなし時)

    Note over WAF: P100: bot-control
    WAF->>WAF: Bot Control ラベル付与<br/>bot:category / bot:name / signal など

    Note over WAF: P150: classify-tier
    WAF->>WAF: x-api-key ヘッダーを検査<br/>pk_enterprise_* → app:tier:enterprise ラベル付与

    Note over WAF: P160: forward-tier
    WAF->>WAF: app:tier: を x-customer-tier ヘッダーに補間

    alt /block-page/ へのリクエスト(P300)
        WAF-->>Client: 403 + HTML Body<br/>IP・リクエスト ID 補間済み
    else /verify へのリクエスト(P400)
        WAF-->>Client: 302 Location: /verify?ip=${awswaf:ip:}&rid=${awswaf:request_id:}
    else その他のリクエスト
        Note over WAF: P500: app-signalling
        WAF->>WAF: Bot Control ラベル 6 種を<br/>x-waf-* ヘッダーに補間

        Note over WAF: P700: cache-segmentation
        WAF->>WAF: bot:category を<br/>x-waf-bot-category ヘッダーに補間

        WAF->>CF: Allow(x-waf-* ヘッダー付き)
        CF->>Lambda: リクエスト転送
        Lambda-->>CF: JSON レスポンス<br/>(wafHeaders を含む)
        CF-->>Client: レスポンス
    end

動作確認結果

GitHub サンプルに含まれるテストケースは以下の6パターンです。

テスト名 関連ルール 確認するシナリオ 何を展開するか 展開先
アプリシグナリング bot-control + app-signalling Bot カテゴリ・名前などをオリジンに転送する Bot Control が付与したラベル(bot:category / bot:name / signal など 6 種) リクエストヘッダー
カスタムラベル転送 classify-tier + forward-tier API キーのプラン情報をオリジンに転送する 自分で付与したカスタムラベル(app:tier:enterprise) リクエストヘッダー
ブロックページ block-with-custom-page ブロックページに IP・リクエスト ID を表示する リクエストコンテキストから生成される合成値(クライアント IP・WAF リクエスト ID) レスポンスボディ(HTML)
リダイレクト verify-redirect リダイレクト先 URL に IP・リクエスト ID を含める リクエストコンテキストから生成される合成値(クライアント IP・WAF リクエスト ID) レスポンスヘッダー(Location)
キャッシュセグメンテーション cache-segmentation ボットカテゴリ別に CloudFront キャッシュを分離する Bot Control が付与したラベル(bot:category) リクエストヘッダー → CloudFront キャッシュキー
ブラウザ vs curl challenge-root + app-signalling WAF トークンのフィンガープリント・トークン ID を確認する WAF トークンから生成されるラベル(fingerprint・token ID)※ブラウザで Challenge 通過後に取得可 リクエストヘッダー

アプリシグナリング

Bot Control がリクエストに付与したラベル(ボットのカテゴリ・名前など)を WAF がリクエストヘッダーに変換してオリジンへ転送します。今回は、Lambda が受け取ったヘッダーをレスポンスで返却しています。

アプリシグナリング ルール 動作確認

(参考)アプリシグナリング ルール

Bot Control のラベル 6 種類がリクエストヘッダーに付与され、 curl コマンドを http_library カテゴリ、ボット名 curl と判定しています。

bot:category の値(advertising / archiver / http_library / search_engine など)は Bot Control ルール一覧 の各ルール説明内の Labels: に記載されていますので詳しくはそちらを参考にしてください。

x-waf-fingerprintx-waf-token-id は WAF トークンが必要なため、curl では空になります(詳細は後述の「ブラウザ vs curl」を参照)。

WAF が挿入したカスタムリクエストヘッダーは、元のリクエストヘッダーと区別するため WAF が自動で x-amzn-waf- プレフィックスを付与してオリジンに届きます。

ヘッダーをログで確認するには、オリジン側でそのヘッダーを出力する設計になっている必要があります。

オリジン 確認方法
Lambda console.log(event.headers) で CloudWatch Logs に出力
EC2 + Nginx $http_x_amzn_waf_x_waf_bot_category 等をアクセスログのフォーマットに追加
EC2 + アプリ アプリ側でリクエストヘッダーを読んで構造化ログに出力

なお、このサンプルは Lambda がレスポンス JSON に wafHeaders をそのまま返す設計のため、curl の出力で直接確認できます。

カスタムラベル転送

x-api-key: pk_enterprise_* を付けてアクセスすると、classify-tierapp:tier:enterprise ラベルを付与し、forward-tier がそれを x-customer-tier ヘッダーとして転送します。

カスタムラベル転送 動作確認

(参考)カスタムラベル転送 ルール

x-customer-tier: enterprise が転送されています。WAF 側で判定を行い、オリジンのアプリはヘッダーを読むだけで済む構成です。

ブロックページ

合成ラベルをレスポンスボディに補間します。今回のアップデート以前はカスタムレスポンスボディは静的な文字列のみで IP やリクエスト ID のような動的な値を埋め込むことはできませんでした。

ブロックページ 動作確認

(参考)ブロックページ ルール

補間された IP アドレスと WAF リクエスト ID を含む HTML ボディとともに 403 を返します。WAF のブロックルール内で定義した HTML テンプレートが、リクエストごとに動的な内容で返却されます。

ブロック時のエラーページにリクエスト ID を表示しておくと、誤検知の問い合わせ対応で「サポートにこの ID を連絡してください」という案内ができます。

リダイレクト

合成ラベルをレスポンスヘッダーに補間します。従来のカスタムレスポンスヘッダーは静的な値しか設定できず、リダイレクト先 URL にリクエストの情報を動的に含めることはできませんでした。

リダイレクト 動作確認

(参考)リダイレクト ルール
302 の Location ヘッダーに IP アドレスとリクエスト ID がクエリパラメータとして展開されています。不審なリクエストを検証ページにリダイレクトしつつ、コンテキスト情報を引き渡す場面で使えます。

キャッシュセグメンテーション

キャッシュセグメンテーション 動作確認

(参考)キャッシュセグメンテーション ルール

CloudFront のキャッシュポリシーで x-amzn-waf-x-waf-bot-category をキャッシュキーに含めているため、Bot カテゴリが異なるリクエストは別々のキャッシュエントリとして扱われます。

(参考)CloudFront のキャッシュポリシー

ブラウザ vs curl

curl では空だった x-waf-fingerprintx-waf-token-id は、WAF トークンを持つリクエストで設定を確認します。

ブラウザで /(ドキュメントルート) を開き、 challenge-root ルールが JavaScript Challenge を返します。通過すると aws-waf-token Cookie が発行されます。その状態で /app-signalling/ にアクセスすると、フィンガープリントとトークン ID が埋まります。

ブラウザでのリクエスト確認(ドキュメントルート)

challenge.js のレスポンス

ブラウザでのリクエスト確認

x-waf-bot-category が空になっています。今回のテストでは正規ブラウザで Challenge を通過した後のアクセスだったため空になりましたが、Bot Control Targeted がヘッドレスブラウザや自動化ツールと判定した場合は automated_browser などの値が入ります(Bot Control ラベル一覧)。フィンガープリントとトークン ID は AWS WAF トークンから取得されるため、Challenge 通過済みのブラウザでは値が設定されます。

フィンガープリントのユースケース例:ログイン画面のクレデンシャルスタッフィング対策

x-waf-fingerprint はデバイス・ブラウザ固有の値であり、セッションをまたいでも、同一ブラウザ・同一デバイス由来のリクエストを継続的に識別するシグナルとして利用できます。

1回目のログイン試行
  ブラウザ → CloudFront /login
           → WAF Challenge 実行
           → AWS WAF トークン発行
  フィンガープリント: abc123 がトークンに紐づく

Cookie / Token 有効期限切れ後

再度ログイン試行
  ブラウザ → CloudFront /login
           → WAF Challenge 再実行
           → AWS WAF トークン再発行
  フィンガープリント: abc123
           → 同一ブラウザ・同一デバイス由来の可能性が高いと判断

ログイン画面に Challenge を設定しておくと、トークンを持つリクエストのフィンガープリントをオリジンへ転送できます。アプリ側で「このフィンガープリントは過去5分間で10回ログイン失敗している」と判定し、CAPTCHA 表示やアカウントロックを行うといった実装もできそうです。

また、IP ベースのレート制限だけでは VPN / Proxy によるローテーションで回避される可能性がありますが、デバイスフィンガープリントは変更が難しく、回避耐性が高くなります。そのため、クレデンシャルスタッフィング攻撃(盗んだ ID / パスワードを大量に試す攻撃)への対策でも活用できそうです。

フィンガープリントはブラウザ・デバイスの特性から計算されるため、同一ブラウザ・同一デバイスでは同じフィンガープリントが継続して利用される可能性があります(実際にシークレットモードで複数回試したところ、同じ値になりました)。

参考ですが、AWS 公式ドキュメント には、「Challenge の JavaScript がブラウザを能動的に検査した結果をトークンへ格納する」といった記載もあります。

The scripts actively interrogate the browser and put the results into the token.

ただし、AWS はセキュリティ上の理由でトークンの詳細仕様を非公開としているので厳密な挙動はブラックボックスです。別ブラウザへの切り替えや指紋偽装ツールによって回避される可能性もあるため、あくまでシグナルの1つとして、ログイン失敗回数・IP・時間帯など他の情報と組み合わせて使う設計が現実的と考えます。

サンプル CDK 実装上の注意点

Lambda Function URL と OAC の組み合わせ(既知の問題)

このサンプルはオリジンに Lambda Function URL を使い、FunctionUrlOrigin.withOriginAccessControl で OAC を設定する構成になっています。

しかし、CDK(v2.170.0〜v2.256.0 で確認)が生成する Lambda リソースポリシーには lambda:FunctionUrlAuthType 条件が含まれておらず、CloudFront OAC からのリクエストが AccessDeniedException で拒否されるケースがあります。

CDK が生成するポリシー(問題あり)

"Condition": {
  "ArnLike": { "AWS:SourceArn": "arn:aws:cloudfront::..." }
}

正しい形(aws lambda add-permission --function-url-auth-type AWS_IAM で生成されるもの)

"Condition": {
  "StringEquals": { "lambda:FunctionUrlAuthType": "AWS_IAM" }
}

■ 補足
CloudFront OAC が Lambda Function URL を呼ぶ際、aws:SourceArn コンテキストが設定されないため、ArnLike: AWS:SourceArn 条件が常に失敗します。

動的ラベル補間の動作確認目的であれば FunctionUrlAuthType.NONE にして OAC を外すのが手軽です。もしくは、CDK デプロイ後に aws lambda add-permission --function-url-auth-type AWS_IAM で permission を差し替える必要があります。

まとめ

AWS WAF の動的ラベル補間(Dynamic Label Interpolation)についての機能概要と実際の動作を紹介しました。

  • ${namespace:} 構文を利用して、ラベル値をカスタムヘッダー・レスポンスヘッダー・レスポンスボディへ動的に展開できる
  • 合成ラベル(${awswaf:ip:} など)を利用することで、リクエストコンテキストの値も扱える
  • カスタムラベルを補間する場合は ${awswaf:<ACCOUNT_ID>:webacl:<WEBACL_NAME>:...} 形式が必要
  • Dynamic Label Interpolation 自体に追加料金はなく、全リージョンで利用可能
  • Bot Control と組み合わせることで、ボット分類情報をアプリケーション側へ連携できる

ブロックページへのリクエスト ID 埋め込みはシンプルかつ実用的で、問い合わせ対応やトラブルシューティングにも活用しやすいと感じました。

個人的には、Bot Control でボットと判定して Block した際に、IP アドレスやリクエスト ID を動的に埋め込んだレスポンスを返し、通常ユーザー向けには問い合わせ先のみを返すような実装も簡単に行えそうだと感じました。

また、Bot Control を利用している環境では、アプリケーションシグナリングと組み合わせることで、WAF の判定結果をアプリケーション層へ連携し、認証・不正アクセス対策・キャッシュ制御などへ活用できそうです。

本記事が参考になれば幸いです。

近藤 諒都

(記事一覧)

カスタマーサクセス部CS5課

夜行性ではありません。朝活派です。

趣味:お酒、旅行、バスケ、掃除、家庭用パン作り(ピザも)など

2025 Japan AWS All Certifications Engineers