-- Views
May 31, 26
スライド概要
JAWS-UG彩の国埼玉支部 #8 彩の国埼玉支部 1周年
https://jawsug-sainokuni-saitama.connpass.com/event/387763/
あなたが知らなそうな AWS WAFの話 2026/5/30 JAWS-UG #8 raiha(Ryo Aihara) / @raiha_tec 彩の国埼玉支部 彩の国埼玉支部 1周年
aws sts get-caller-identity 仕事 SOC運用やログ分析基盤を作ってます 趣味 (最近やってないけど)自作スピーカー / 自作キーボード AI/ローカルLLMで遊ぶ 自作Aqua Voice Google MeetでVTuberするChrome拡張(右下) 𝕏: @raiha_tec LT 彩の国埼玉支部 2回目(#0、今回) Slidevでの発表 2回目 Security-JAWS CfP落ちの内容を話します 好きなAWSサービス ECS / CDK 埼玉歴30数年 かわいい
本日の立ち位置 👶 初心者 ▶ 📰 電通総研ブログ ▶ 🧑💻 中級者 ┊ 横道 ▶ 🧙 上級者 🌳 今日の話 📰 電通総研ブログ(前提) 「AWS WAF について最初から知りたかったこと8選」 = 初心者:Lv200と中級者:Lv300の間ぐらい? 8 KB / マネージドルールのバージョン / etc. 📖 電通総研ブログ:AWS WAF について最初から知りたかったこと8選 🌳 今日の話(このLT) どちらかといえば中級者だが、なくても困らない…か? 明日からすぐ使える!系の話はないです
①もはや知られていないことで有名な8KB 「 ※ AWS WAF Body の例 について最初から知りたかったこと8選」にも記載あり 0KB 8KB 16KB ... 50KB │ │ │ │ ████████ ← ここまでしか見ない (8 KB まで) ────────┃ > 8,192 B で Block ※Count に下げると無効 ████████████████████████████████████████ └─ padding ─┘└── ' OR 1=1 -- ──────────┘ (ダミー) (8 KB 以降の死角に隠す) WAF 本体検査(ALB) CRS Block 閾値 攻撃者の細工 → SQLi / XSS のマネージドルールも 8 KB 以降は見ない。攻撃部分が完全に WAF の死角に入る 📏 WAF 仕様上限(Body) : 固定(拡張不可) CloudFront / APIGW 等:16 KB → 最大 64 KB へ拡張可 Body部のうち、先頭8~64KBまでをAWS WAFは検査する ALB / AppSync 8 KB 📖 AWS Docs: Oversize request components in AWS WAF ⚠️ CommonRuleSetの誤った理解 は > 8,192 B で Block → 単に大きいリクエストを拒否するルールと考えると… → Count にすると「8KB以降のBody部が検査されない」 = 攻撃されやすい・気づきづらい `SizeRestrictions_BODY`
①続 サイズは書けるが、個数は書けない `Cookie:` 🍪 1 🍪 2 🍪 3 … 🍪 200 ┃ 💣 201 💣 202 … 👁️ WAF が見る範囲(先頭 200 個) ✅ サイズはカスタムで書ける SizeConstraintStatement: FieldToMatch: Body: { OversizeHandling: MATCH } ComparisonOperator: GT Size: 16384 # ← 16 KB 超を Block 🙈 見えない領域(201 個目以降) ❌ 個数で書く手段がない やりたいこと Cookie 可否 が 201 個以上 で Block ❌ が 201 個以上 で Block ❌ Header 💡 `SizeConstraintStatement` は Content-Length ベースなの で WAF 側に「先頭 200 個まで」の 検査上限 があるのに、 検査範囲(8KB)を超えるサイズでも書ける statement に 「個数を数える」プリミティブが無い → CRS の `SizeRestrictions_BODY` (8KB) より大きい閾値も自 → 検査上限を超えた死角を、個数で塞げない 由に設定可 🚨 小さな Cookie を 200 個以上詰めれば、201 個目以降にペイロードを隠せる
🧪 FieldToMatch × OversizeHandling CloudFront + WAF で「Header 値に `BLOCKME` を含めば Block」のルールを構成。ダミー Header の数と marker 位置を変えて curl で検証 検査方式 OversizeHandling (名前指定) が範囲内 ≤ 個目) marker ( 200 が範囲外 ≥ 個目) marker 201 marker ( 201 + 無し 個超 (n/a) 🛑 Block 🛑 Block ✅ Allow any 🛑 Block 🛑 Block ✅ Allow `Headers` + `All` `CONTINUE` (既定) 🛑 Block ✅ Bypass ✅ Allow `Headers` + `All` `NO_MATCH` 🛑 Block ✅ Bypass ✅ Allow `Headers` + `All` `MATCH` 🛑 Block 🛑 Block 🛑 誤検知 Block `SingleHeader` `Headers` + `IncludedHeaders` 🔑 名前指定(SingleHeader / IncludedHeaders)は 200 個制限の対象外 — 名前で直接取り出すため位置と無関係 🔑 全件スキャン(Headers / All)のみ 200 個制限が効く — 既定の `CONTINUE` は バイパス余地アリ、 `MATCH` はバイパス潰せるが正 規 200 個超ユーザを誤検知
🛠️ ② boto3 で WAF 更新時の LockToken
WAF
は 楽観ロックでリソース管理。 `LockToken` を握っていないと更新が拒否される
❌ LockTokenなし
✅ `get` で取得 `update` に渡す
import boto3
c = boto3.client('wafv2')
# 1. get で LockToken を取得
r = c.get_web_acl(Name='my-acl',
Scope='REGIONAL', Id='abc123')
lt = r['LockToken'] # ← これ
c.update_web_acl(
Name='my-acl', Scope='REGIONAL',
Id='abc123',
DefaultAction={'Allow': {}},
Rules=[...], VisibilityConfig={...},
)
#
WAFOptimisticLockException
💥
# 2. update に LockToken 同梱
c.update_web_acl(
Name='my-acl', Scope='REGIONAL', Id='abc123',
DefaultAction={'Allow': {}},
Rules=r['WebACL']['Rules'],
VisibilityConfig=r['WebACL']['VisibilityConfig'],
LockToken=lt, # ← 必須
)
🔑 Web ACL / Rule Group / IP Set / Regex Pattern Set すべて LockToken 必須。 `WAFOptimisticLockException` を食らったら再 `get` してリトライ
📐 WCU: WebACL Capacity Unit 雑に言うと ルールの大きさ 。ルールが複雑だと必要なWCUは大きくなる。 🧱 3階層の WCU 上限 Web ACL 💡 知らないとハマるポイント ─┬─ 基本料金で 1,500 WCU ├─ 最大 5,000 WCU └─ 超過分は 追加課金 Rule Group ┬─ 最大 5,000 WCU └─ 作成時に Capacity 確定 (immutable / 変更不可) Rule ─────┬─ タイプ毎に WCU が違う ├─ SizeConstraint → 低 └─ Regex / JsonBody → 高 の はimmutable → 中の ルール追加 / 削除 / 更新は自由 → ただし合計 WCU は宣言値内に収める必要あり → 超過する場合は Rule Group ごと作り直し Rule Grop WCU 🔒 に載せた Rule Group のコストは 実 WCU ではなく宣言した Capacity 値で固定 → 固定されているので、Rule Group内を自由に更新しても、Web ACLにル ールを載せられる。 Web ACL transformation / JSON body inspection 🔑 WCU は「実際の検査内容には影響しない、AWS 側のリソース予約」。だから事前計画が大事 を足すとルール WCU が 増加
🛠️ ③ `check_capacity` で WCUの計算
デプロイする前にWCUの試算ができる
引数のルールを 構文 validation という効果も
✅ `check_capacity` の使い方
💡 構文 validation も同時に走る
import boto3
c = boto3.client('wafv2')
resp = c.check_capacity(
Scope='REGIONAL', # or CLOUDFRONT
Rules=[{'Name': 'block-bad-bots',
'Priority': 1,
'Action': {'Block': {}},
'Statement': {...},
'VisibilityConfig': {...}}])
print(resp['Capacity']) # → 5 WCU
引数の `Rules` をパースするので、不正なルールがあると
`WAFInvalidParameterException` を返してくる
検出される構文エラー(例):
ネスト不可な statement のネスト
`OR Statement` にネスト1個だけ
不正な `FieldToMatch` / パラメータ値
→ Dry-run として CI に仕込めば WCU 超過 + 構文ミスを同時検査(IPSet ARN
実在性などは別 API)
🔑 マネージドルールは `describe_managed_rule_group` で個別 WCU を取得 → `check_capacity` に渡して合算試算もできる
🎲 例題:このルール、何 WCU? rule1 — JP × `.*/test/.*` rule2 — US × `.*/example/.*` { { } "Statement": { "AndStatement": { "Statements": [ { "GeoMatchStatement": { "CountryCodes": ["JP"] } }, { "RegexMatchStatement": { "RegexString": ".*/test/.*", "FieldToMatch": { "Body": {...} }, "TextTransformations": [ { "Priority": 0, "Type": "URL_DECODE" }, { "Priority": 1, "Type": "LOWERCASE" } ] } } ] } } } "Statement": { "AndStatement": { "Statements": [ { "GeoMatchStatement": { "CountryCodes": ["US"] } }, { "RegexMatchStatement": { "RegexString": ".*/example/.*", "FieldToMatch": { "Body": {...} }, "TextTransformations": [ { "Priority": 0, "Type": "URL_DECODE" }, { "Priority": 1, "Type": "LOWERCASE" } ] } } ] } } 🤔 両方を同じ Web ACLに載せたとき、消費 WCU は何? 単純合計と `check_capacity` の結果は一致する?
🎲 例題:まずは 1 ルールの WCU 📖 公式表のWCUを使って rule1 / rule2 の WCU を積み上げてみる 構成要素 公式表の WCU 内訳 `GeoMatchStatement` ( `JP` or `US` ) 1 1 WCU `RegexMatchStatement` (Body) 3 3 WCU `URL_DECODE` transformation +10 10 WCU `LOWERCASE` transformation +10 10 WCU 集約) `AndStatement` ( 子の合計 — = 24 WCU → rule1 = 24 WCU / rule2 = 24 WCU …なので 2 ルールの合計は?
🎲 例題:答え合わせ
✨ `check_capacity` で実測すると… 💡 なぜ 20 WCU 減るのか
c.check_capacity(
Scope='REGIONAL',
Rules=[rule1, rule2]
)
# → {'Capacity': 28}
単純合計
実測値
差分
48 WCU ████████████████
28 WCU █████████
-20 WCU ← 最適化分
両ルールが同じ条件を満たしている:
同じ component(Body)を検査
同じ transformation(URL_DECODE + LOWERCASE)を適用
→ AWS WAF は 変換処理を 1 回にまとめる
→ 2 ルール分の transformation コスト
`(10 + 10) × 2 = 40` のうち 半分の 20 WCU が削減
🔑 同じ component に同じ transformation を当てるルールが増えても WCU はリニアに増えない。Web ACL 内 / Rule Group 内のどちらでも最適化される
🎲 最適化が効く / 効かないパターン 同じ rule1 / rule2 でも 配置の仕方 で最適化の効き方が変わる ✅ パターン A Web ACL に直接 2 ルール ┌─ Web ACL ─────────┐ │ rule1 │ │ rule2 │ └───────────────────┘ → 28 WCU ✨ (最適化が効く) ✅ パターン B ❌ パターン C つの Rule Group 別々の Rule Group ┌─ Web ACL ─────────┐ │ ┌─ RuleGroup ───┐ │ │ │ rule1 │ │ │ │ rule2 │ │ │ └───────────────┘ │ └───────────────────┘ ┌─ Web ACL ─────────┐ │ ┌─ RG-A ────┐ │ │ │ rule1 │ │ │ └───────────┘ │ │ ┌─ RG-B ────┐ │ │ │ rule2 │ │ │ └───────────┘ │ 1 観点 RG WCU 内(実消費) Web ACL 視点 └───────────────────┘ 28 ✨ 観点 RG の 宣言 Capacity 値 RG-A / RG-B → RG 内では最適化が効く(実 WCU が下がる) WCU 内 Web ACL 点 視 24 / 24 各 RG の 宣言 Capacity 値の 合計
🛠️ ④ 今ブロック中の IP を覗く `get_rate_based_statement_managed_keys` 件) 📋 何が取れるか リスト(現在 にハマってる ) IPv4 / IPv6 rate limit IP 最大 10,000 件。超過分はレート最大のものが残る Web ACL × Rule Group × Rate-based rule 単位で独立管理 ⚠️ 制約 が `IP` or `FORWARDED_IP` の rule のみ `CONSTANT` / `CUSTOM_KEYS` だと `WAFUnsupportedAggregateKeyTypeException` 💥 → Cookie ベース等の rate-limit は不可視 `AggregateKeyType` で 現在 rate-limit にハマっている IP を取得(最大 10,000 🔧 使い方 import boto3 c = boto3.client('wafv2') resp = c.get_rate_based_statement_managed_keys( Scope='REGIONAL', WebACLName='my-acl', WebACLId='abc123', RuleName='block-too-many-reqs', ) print(resp['ManagedKeysIPV4']['Addresses']) # → ['203.0.113.5', '198.51.100.42', ...] print(resp['ManagedKeysIPV6']['Addresses']) 🔑 障害時の「今、誰が引っかかってる?」をログ無しで即時取得可。CloudWatch メトリクスでは IP 単位まで見えない
まとめ の死角 — サイズは弾けるが、個数は弾けない 2. LockToken — boto3 で更新するなら `get` → `update` 3. `check_capacity` — WCUの試算API 4. WCU の話 - テキスト変換のWCU 5. `get_rate_based_statement_managed_keys` — 今ブロック中の IP 1. 8 KB 知らないよりは知っていたほうがいいはず… 📝 話さなかったこと:WAF ログフォーマットの話 — 細かい話で長くなるのでカット
📢 告知 ① — JAWS SONIC 2026 JAWS SONIC 2026 / MIDNIGHT JAWS 2026 - THE MARATHON - 土 〜 9/6 (日) 12:00 / オンライン開催 🌐 時間ぶっ通しの JAWS-UG オンラインイベント 2026/9/5 ( ) 12:00 24 🔗 jaws-ug.connpass.com/event/393837/
📢 告知 ② — JAWS FESTA AKITA 2026 JAWS FESTA AKITA 2026 土 / あきた芸術劇場ミルハス 🎭 秋田さ来てたんせ! 🌾 2026/11/7 ( ) 🔗 jawsfesta2026.jaws-ug.jp
ご清聴ありがとうございました 🙏 💬 質問・感想はお気軽にどうぞ! raiha 𝕏: @raiha_tec