同じ WAF に、同じ宛先へ、同じログイン済みセッションで、2つのリクエストを投げます。SQLインジェクションのペイロードは 403 Forbidden で弾かれ、GET /api/v1/cust/124 は 200 OK で他人の氏名とメールアドレスを返します。違うのはリクエストの「形」だけ。境界は攻撃の形は知っていても、「誰が見ていいか」は知りません。
社内エンジニア向けセキュリティ勉強会(全6回)の第5回です。AWS WAF・セキュリティグループ・ネットワークACL という境界防御を「通信の形を判断する道具」として位置づけ直し、境界で受けられる脅威と、アプリの認可でしか塞げない脅威の線引きに落とします。Laravel 13.x(新規)と FuelPHP 1.x(レガシー)が混在する現場を前提に、どの脅威をどの層で受けるかを設計として示します。
疑似デモは2つ。中身だけを変えて同じ経路に投げます。(A) SQLi ペイロードはマネージドルールのパターンに一致して 403 で止まる。(B) /api/v1/cust/124 は攻撃パターンに一致しないので 200 OK、他人のデータがそのまま返る。片方が止まって片方が通る理由を、WAF は「既知のパターン」を見ていて、SG は「ポート」しか見ていない、という構造まで分解します。
扱うトピック
- 境界の正体:セキュリティグループ=L3/L4 のパケットフィルタ(ENI 単位・ステートフル・許可ルールのみ)、WAF=L7 のアプリケーションレベルゲートウェイ(Web ACL・マネージドルール・Allow / Block(403) / Count)
- なぜ素通しするのか:攻撃パターンに一致しなければ許可されてアプリへ渡る。443 が開いていれば文法的に正当な GET は SG の対象外。加えてマネージドルールにも検査上限がある(ALB・AppSync は 8KB 固定、CloudFront 等は既定 16KB・最大 64KB、ヘッダ/クッキーは 8KB)── Allow ならリクエストは丸ごと転送される
- 脅威×層のマトリクス:SQLi・XSS は WAF で緩和(仮想パッチ=時間稼ぎ)+コードで根治、SSRF は egress 制限+URL 検証+IMDSv2、IDOR/BOLA は境界では止まらずアプリのオブジェクト単位の認可が唯一の砦
- SG と NACL の使い分けと限界:ステートフル/ステートレス、ENI/サブネット。そして AWS が明記するとおり SG も NACL も IMDS(169.254.169.254)宛の通信はフィルタできない ── だから第一の防御は IMDSv2 とホップ制限
持ち帰る3つの結論
1. 境界(WAF/SG)は通信の形=IP・ポート・既知パターンを判断する
2. 中身の正当性(認可)は境界では判断できず、アプリでしか塞げない
3. 脅威ごとに受ける層を決め、多層で守る
レガシー保守(FuelPHP 1.x)でも「明日からできる」棚卸しまで示します(Web ACL とルール・開放ポートの棚卸し、WAF 任せにしている画面/API にアプリ側の認可があるかの確認、WAF ログと VPC フローログの出力を有効化)。Appendix には AWS WAF の構成と検査上限、SG と NACL の詳細比較、ファイアウォールの分類と限界、DMZ・セグメンテーション・IDS/IPS、出典リンク集を付けています。
想定読者:「WAF と SG を入れたから安全」と思い、アプリ側の認可を後回しにしていた"ちょっと前の自分"。
分類:IDOR/BOLA・認可不備 = OWASP A01:2021 Broken Access Control(CWE-639 / CWE-862 / CWE-200)、SQLi・XSS = A03:2021 Injection、SSRF = A10:2021 Server-Side Request Forgery