AWS WAF Dynamic Label Interpolation でボット情報の動的転送とカスタムブロックページを試してみた
はじめに
2026年5月11日、AWS WAF の Dynamic Label Interpolation が、AWS WAF を利用可能なすべてのリージョンで利用可能になりました。さらに7月21日には Security Blog で詳細なユースケースが公開されています。
AWS WAF Bot Control は公式ブログによると650以上のボットおよびエージェントを識別しラベルを付与しますが、従来はラベルごとに個別のルールを書いてヘッダー挿入やレスポンス制御を行う必要がありました。Dynamic Label Interpolation を使うと、ラベル値を ${namespace:} 構文でルール内に埋め込めるため、1つのルールで名前空間全体をカバーできます。本記事では ALB + リージョナル WAF の最小構成で、以下の2つのシナリオを検証しました。
- シナリオ1 — Bot Control が付与するラベルを
${namespace:}で動的にリクエストヘッダーへ転送し、ルール管理コストを削減 - シナリオ2 — Synthetic Labels(IP・リクエストID)をカスタムブロックレスポンスに埋め込み、問い合わせ時の追跡を容易にする
検証内容
検証環境
| 項目 | 内容 |
|---|---|
| クライアント | curl 8.18.0(各種 User-Agent を指定) |
| ALB | 固定レスポンス 200(ターゲットグループ不要) |
| AWS WAF | リージョナル WebACL、Bot Control Common(InspectionLevel: COMMON) |
| Bot Control OverrideAction | Count |
| リージョン | ap-northeast-1 |
| デプロイ方法 | CloudFormation |
WAF のルール評価順序は priority の昇順です。本検証では以下の順で評価されます。
bot-control-common(priority: 0)— Bot Control マネージドルールグループ。ラベルを付与するがブロックしない(OverrideAction: Count)forward-bot-signals(priority: 100)— カスタムルール。Bot Control が付与したラベルを参照しヘッダーを挿入rate-limit-with-debug-page(priority: 200)— レートベースルール。閾値超過時にカスタムブロックページで応答
後続ルールでラベルを参照するため、Bot Control は OverrideAction: Count に設定します。詳細は「初回検証で得た前提条件」を参照してください。
シナリオ1: Bot Control ラベルの動的転送
ルール設定
forward-bot-signals ルールでは、LabelMatchStatement の Scope: NAMESPACE で名前空間全体にマッチし、${namespace:} 構文でラベル値をヘッダーへ動的展開しています。
forward-bot-signals ルール定義(JSON)
{
"Name": "forward-bot-signals",
"Priority": 100,
"Statement": {
"LabelMatchStatement": {
"Scope": "NAMESPACE",
"Key": "awswaf:managed:aws:bot-control:bot:category:"
}
},
"Action": {
"Count": {
"CustomRequestHandling": {
"InsertHeaders": [
{
"Name": "x-waf-bot-category",
"Value": "${awswaf:managed:aws:bot-control:bot:category:}"
},
{
"Name": "x-waf-bot-name",
"Value": "${awswaf:managed:aws:bot-control:bot:name:}"
},
{
"Name": "x-waf-bot-signals",
"Value": "${awswaf:managed:aws:bot-control:signal:}"
},
{
"Name": "x-waf-client-ip",
"Value": "${awswaf:ip:}"
}
]
}
}
},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "forward-bot-signals"
}
}
テスト結果
3つの User-Agent パターンでリクエストを送信し、Bot Control のラベル付与と forward-bot-signals の動作を確認しました。
| テスト | User-Agent | HTTP Status | Bot Control 判定 | forward-bot-signals |
|---|---|---|---|---|
| テスト1: curl デフォルトUA | curl/8.18.0 | 200 | bot:category:http_library + signal:non_browser_user_agent |
発火(ヘッダー挿入) |
| テスト2: Googlebot 偽装 | Googlebot/2.1 | 200 | signal:non_browser_user_agent |
不発火 |
| テスト3: Chrome UA | Chrome/126.0 | 200 | —(token:absent のみ) |
不発火 |
テスト1では Bot Control が bot:category:http_library ラベルを付与し、forward-bot-signals の条件に合致したためヘッダー挿入が実行されました。WAF ログの requestHeadersInserted には展開済みの値が記録されています。
"requestHeadersInserted": [
{"name": "x-amzn-waf-x-waf-bot-category", "value": "http_library"},
{"name": "x-amzn-waf-x-waf-bot-name", "value": "curl"},
{"name": "x-amzn-waf-x-waf-bot-signals", "value": "non_browser_user_agent"},
{"name": "x-amzn-waf-x-waf-client-ip", "value": "203.0.113.1"}
]
${namespace:} 構文と展開結果の対応は以下のとおりです。
| 変数(namespace 補間) | 展開結果 |
|---|---|
${awswaf:managed:aws:bot-control:bot:category:} |
http_library |
${awswaf:managed:aws:bot-control:bot:name:} |
curl |
${awswaf:managed:aws:bot-control:signal:} |
non_browser_user_agent |
${awswaf:ip:} |
203.0.113.1 |
テスト2(Googlebot 偽装)では、Bot Control は signal:non_browser_user_agent ラベルを付与しましたが、bot:category: ラベルは付与しませんでした。Bot Control はボットの識別に User-Agent 文字列だけでなく送信元 IP などのリクエスト特性も利用するため、本検証では UA の変更だけで bot:category:search_engine に分類されることは確認できませんでした。forward-bot-signals は bot:category: 名前空間を条件としているため、ルールが発火しません。
テスト3(Chrome UA)は Bot Control のシグナルラベルが付かず token:absent(AWS WAF トークンがリクエストに存在しない)のみ付与されるため、同様に bot:category: 名前空間にマッチせず不発火です。
なお、公式ドキュメントによると、名前空間に複数のラベルがマッチした場合はカンマ区切りで連結されますが、今回の検証では該当状況が発生せず未確認です。
初回検証で得た前提条件
最初の検証では OverrideAction を None(マネージドルールのアクションをそのまま適用)に設定していました。その結果、Bot Control 内の CategoryHttpLibrary ルールがリクエストを BLOCK し、後続の forward-bot-signals に到達しませんでした。WAF ログには Bot Control による BLOCK のみが記録され、requestHeadersInserted フィールドは出現しません。OverrideAction を Count に変更すると、Bot Control はラベル付与のみを行いリクエストを通過させるため、後続ルールがラベルを参照できるようになります。
WAF ログ: OverrideAction: None 時の BLOCK(テスト1相当)
{
"terminatingRuleId": "bot-control-common",
"terminatingRuleType": "MANAGED_RULE_GROUP",
"action": "BLOCK",
"ruleGroupList": [{
"ruleGroupId": "AWS#AWSManagedRulesBotControlRuleSet",
"terminatingRule": {
"ruleId": "CategoryHttpLibrary",
"action": "BLOCK"
}
}],
"labels": [
{"name": "awswaf:managed:aws:bot-control:bot:category:http_library"},
{"name": "awswaf:managed:aws:bot-control:bot:name:curl"},
{"name": "awswaf:managed:aws:bot-control:signal:non_browser_user_agent"}
]
}
シナリオ2: カスタムブロックページへの Synthetic Labels 埋め込み
レートベースルール rate-limit-with-debug-page は、5分間に100リクエストを超過した IP を BLOCK し、カスタムレスポンスボディで応答します。レスポンスボディとヘッダーに Synthetic Labels(${awswaf:ip:} と ${awswaf:request_id:})を埋め込み、ブロック時のサポート問い合わせに必要な情報を動的に表示します。
{
"CustomResponseBodies": {
"block-page-with-debug": {
"Content": "<!DOCTYPE html>\n<html>\n<head><title>Access Denied</title></head>\n<body>\n<h1>Access Denied</h1>\n<p>Your IP: ${awswaf:ip:}</p>\n<p>Request ID: ${awswaf:request_id:}</p>\n<p>If you believe this is an error, contact support with the Request ID above.</p>\n</body>\n</html>",
"ContentType": "TEXT_HTML"
}
}
}
テスト結果
Chrome UA で110回連続リクエストを送信してレートリミットを発火させ、ブロックレスポンスを確認しました。レートベースルールは直近5分間を評価ウィンドウとしてリクエストレートを継続的に評価し、しきい値を超えている間は BLOCK されます。しきい値を下回ったと判定されるとブロックは解除されます。
ブロック時にクライアントが受け取った HTML レスポンスボディです。
<!DOCTYPE html>
<html>
<head><title>Access Denied</title></head>
<body>
<h1>Access Denied</h1>
<p>Your IP: 203.0.113.1</p>
<p>Request ID: 1467396511:1-6a5fdb1c-7efa072b7f5ecd8c3bb60a6c</p>
<p>If you believe this is an error, contact support with the Request ID above.</p>
</body>
</html>
レスポンスヘッダー x-blocked-request-id にも同じ Request ID が挿入されています。
x-blocked-request-id: 1467396511:1-6a5fdb1c-7efa072b7f5ecd8c3bb60a6c
本検証では、レスポンスに表示された Request ID と WAF ログの requestId フィールドが一致しました。このため、ユーザーから報告された ID で該当する WAF ログを特定できます。
WAF ログ: レートリミット BLOCK(テスト4)
{
"terminatingRuleId": "rate-limit-with-debug-page",
"terminatingRuleType": "RATE_BASED",
"action": "BLOCK",
"responseCodeSent": 403,
"rateBasedRuleList": [{
"rateBasedRuleName": "rate-limit-with-debug-page",
"limitKey": "IP",
"maxRateAllowed": 100,
"evaluationWindowSec": 300,
"limitValue": "203.0.113.1"
}]
}
まとめ
AWS WAF Bot Control を使ってボット種別ごとに制御を追加していくと、カテゴリごとの判定ルールや例外設定が増え、WebACL の管理が複雑になりがちです。Dynamic Label Interpolation は、Bot Control が付与したカテゴリ・名称・シグナルを名前空間単位でリクエストヘッダーへ動的に転送できるため、カテゴリごとの値を WAF ルールに列挙せず、オリジン側で利用するためのシンプルな受け渡し手段になります。
これにより、WAF でカテゴリごとの処理を細かく書き分ける代わりに、判定結果をオリジンへ渡し、アプリケーション側で必要な処理や応答を選択する構成を検討できます。今回の検証では、Bot Control のラベル値がヘッダーへ動的に展開されることを確認しました。
また、Bot Control を含む WAF ルールでは、意図しないブロックや問い合わせ対応が発生することもあります。Synthetic Labels をカスタムブロックページやレスポンスヘッダーに埋め込むことで、ブロックされた利用者から Request ID を受け取り、対応する WAF ログを追跡するための手掛かりとして利用できます。
Bot Control のルール数や例外管理、ブロック時の調査に課題を感じている場合は、まず検証環境で Dynamic Label Interpolation をお試しください。








