WAF・DDoS対策が本番で失敗した話|AWS WAF v2の地雷と3年の運用知見

AWS WAFを本格導入した際のFalse Positive地獄とレート制限の失敗事例を、3年の運用経験からぶっちゃけます。実装で引っかかるポイントを先回りで解説。

WAF・DDoS対策2026|本番導入で学んだ実装の地雷と運用の本当

先日プロジェクトでWAFとDDoS対策を本格導入したんですよね。正直、事前のセキュリティレビューでは「AWS WAFとShield Advancedを入れとけば大丈夫」って感じだったんですけど、実際に運用が始まると、想像以上に複雑でした。特にFalse Positiveとの付き合い方レート制限の設計で、めちゃくちゃハマりました。

うちのチームは過去に何度か不正アクセスの検知遅れで、信頼を失いかけた経験があるんです。だから今回は「ちょうどいい加減」じゃなくて「きちんと運用できるレベル」で構築することにしました。その過程で見えたこと、失敗したことを共有したいと思います。

AWS WAF v2の基本と実装で最初に引っかかったこと

AWS WAFの仕組みは理解していたつもりですが、実装段階で「あ、これ思ったより細かいな」って感じました。WAFのルールベースは大きく3つの層があります。

AWS マネージドルール — AWSが定期更新するOWASPベース、カスタムルール — IP制限やレート制限、IPレピュテーションリスト — 既知の悪質なIPの自動ブロック。こういった層構造になっているんですよね。

うちが最初にやってしまったのは、マネージドルールを全部有効化して、すぐに本番に流したことです。結果、正規ユーザーからのリクエストが大量にブロックされました。特に海外ユーザーが使うかもしれないAPI経由のアクセスが、SQLインジェクション検知ルールに引っかかり始めたんです。

実は、マネージドルールって「セキュリティ度100%」じゃなくて、業務に合わせて調整する前提なんですよね。自分たちのAPIがどんなパターンでリクエストを送るのか、まずそれを把握しないとダメでした。

{
  "Name": "AWSManagedRulesCommonRuleSet",
  "Priority": 1,
  "Statement": {
    "ManagedRuleGroupStatement": {
      "VendorName": "AWS",
      "Name": "AWSManagedRulesCommonRuleSet",
      "ExcludedRules": [
        {
          "Name": "SizeRestrictions_BODY"
        },
        {
          "Name": "GenericRFI_BODY"
        }
      ]
    }
  },
  "OverrideAction": {
    "None": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "AWSManagedRulesCommonRuleSet"
  }
}

こんな感じで、除外ルールを指定することになります。うちは最初「SizeRestrictions_BODY」を除外しました。理由は、ファイルアップロード機能で正規の大容量リクエストが弾かれたから。その後、アクセスログを分析して、3週間かけて10個のルール調整を加えました。地味に大変でしたよ。

CloudFront × Shield Advanced × WAFの組み合わせで学んだこと

DDoS対策といったら、AWS Shield Advancedを思い浮かべる人が多いと思うんです。でも正直な話、Shield Advancedだけじゃ不十分で、CloudFront + WAF + Shield Advancedの3層設計になってようやく実用的な対策になるんですよね。

うちが本番前に検証したのは、こういう構成です:

graph TB
  Internet["Internet"] -->|DDoS Attack| CloudFront["CloudFront<br/>Layer 1: Shield Standard"]
  CloudFront -->|Filtered Requests| WAF["AWS WAF v2<br/>Layer 2: Custom Rules"]
  WAF -->|Safe Traffic| ShieldAdv["Shield Advanced<br/>Layer 3: DDoS Detection"]
  ShieldAdv -->|Clean Traffic| ALB["Application Load Balancer"]
  ALB -->|Internal| App["Application Servers"]
  
  CloudFront -.->|Real-time Metrics| CloudWatch["CloudWatch<br/>Monitoring"]
  WAF -.->|WAF Logs| S3["S3<br/>Log Storage"]
  ShieldAdv -.->|Advanced Metrics| ShieldConsole["Shield Console"]

CloudFront単体だと、キャッシュ攻撃には強いんですが、Origin直撃のDDoS(CloudFrontをバイパスされるケース)に対応できません。それにWAFを組み合わせて、アプリケーション層の攻撃を検知・防止します。そしてShield Advancedで、より高度なボリュメトリック攻撃を検知します。

ただ、この3層設計が本当に重いんですよ。特にCloudFrontのキャッシュ戦略とWAFの相性がシビアでした。キャッシュキーの設定がちょっとズレるだけで、WAFのルール評価がおかしくなったりするんです。

レート制限で本番が一度死んだ話

正直、ここが一番痛い経験です。うちは「1IPあたり1秒間に100リクエスト」っていうレート制限ルールを設定しました。シンプルで、攻撃的なスクレイピングを止めるには十分だと思ったんです。

ですが、本番流してから30分で、正規ユーザーから大量の「403 Forbidden」エラーが報告されました。原因はバッチ処理です。あるユーザーが、正規の範囲内でAPIを連続呼び出ししていただけなんです。その方は、DataGridで50行のデータをロードするのに、APIを60回呼び出していた。つまり、0.83秒間に60リクエストですね。

# こういうクライアント側の実装があった
for row in data_rows:
    response = await api.fetch(f"/api/data/{row.id}")
    # キャッシュなし、連続呼び出し
    process(response)

レート制限を設計するには、実装側も考慮する必要があります。うちはこの失敗から、以下の対策を入れました。

APIレスポンスにキャッシュヘッダー追加 — クライアント側でも一時的にキャッシュするよう促す。バッチAPI新設 — 複数データを一度に取得できるエンドポイント実装。段階的なレート制限 — 初日は200req/sec、1週間後に100req/secに下げるという戦略です。

{
  "Name": "RateLimitRule",
  "Priority": 2,
  "Statement": {
    "RateBasedStatement": {
      "Limit": 2000,
      "AggregateKeyType": "IP",
      "ScopeDownStatement": {
        "ByteMatchStatement": {
          "SearchString": "/api/",
          "FieldToMatch": {
            "UriPath": {}
          },
          "TextTransformation": "LOWERCASE",
          "PositionalConstraint": "STARTS_WITH"
        }
      }
    }
  },
  "Action": {
    "Block": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "RateLimitRule"
  }
}

2000(Limit)= 2秒間に200リクエストという意味です。この設定で、正規のバッチ処理は許可しつつ、攻撃的なボットはブロックできるようになりました。

IPホワイトリスト管理の運用地獄

WAFでよくある実装パターンが、特定のIPからのアクセスを無条件に許可することです。例えば、社内VPNのIP、パートナー企業のIPなんかですね。

うちも最初、「ホワイトリストIPは全ルールをスキップする」という設定にしていました。

{
  "Name": "IPWhitelist",
  "Priority": 0,
  "Statement": {
    "IPSetReferenceStatement": {
      "Arn": "arn:aws:wafv2:ap-northeast-1:xxx:regional/ipset/whitelist-ips/xxx"
    }
  },
  "Action": {
    "Allow": {}
  },
  "OverrideAction": {
    "None": {}
  }
}

ただね、パートナー企業のネットワーク構成が変わるたびに、IP追加依頼が来るんです。それを手作業で対応していたら、1ヶ月で30IPぐらい溜まりました。その後、うっかり1つのIPを削除したら、パートナーの本番が止まってしまった。ほんと冷や汗ですよ。

インシデント対応のベストプラクティス2026でも触れていますが、こういった「手作業の設定変更」は、ログと承認フロー無しでやるべきではありません。

今は、以下のように改善しました。IPホワイトリスト更新APIを作成して、チケット管理と連動させるAWS Systems Manager Parameter Storeに保存し、IaCでバージョン管理する自動テストで、ホワイトリストIP経由でアクセス可能かテストする。この3つが揃って初めて安心なんですよね。

# IPホワイトリスト更新の自動化例
import boto3
import json

waf_client = boto3.client('wafv2')
ssm_client = boto3.client('ssm')

def update_ipset_from_parameter_store():
    # Parameter Storeからホワイトリストを取得
    response = ssm_client.get_parameter(
        Name='/waf/whitelist-ips',
        WithDecryption=False
    )
    whitelist_ips = json.loads(response['Parameter']['Value'])
    
    # WAFのIPセットを更新
    waf_client.update_ip_set(
        Name='whitelist-ips',
        Scope='REGIONAL',
        Id='xxx-xxx-xxx',
        Addresses=whitelist_ips['addresses'],
        LockToken='xxx'
    )
    
    print(f"Updated {len(whitelist_ips['addresses'])} IPs")

if __name__ == "__main__":
    update_ipset_from_parameter_store()

False PositiveとBlockedリクエストの監視

WAFを本番に入れたら、必ずやることがログの監視と分析です。知られていないんですが、WAFのログだけで月に数GBになるんですよ。それをクエリするのが大変。

うちは以下のような構成で監視しています。CloudWatch Logsに全ブロックリクエストを送信して、Athenaで集計分析するんですね。

xychart-beta
    title "WAF Blocked Requests Daily (2026年6月)"
    x-axis [6/1, 6/2, 6/3, 6/4, 6/5, 6/6, 6/7]
    y-axis "Requests" 0 --> 5000
    line [120, 145, 312, 98, 87, 156, 203]

このデータから、6/3に異常があったことがわかります。原因は、API仕様が変わったのに、古いクライアントアプリが動いてたからです。WAFのSQLインジェクション検知ルールが、パラメータフォーマットの違いで引っかかっていました。

CloudWatch Logsにフィルターをセットして、毎日10件以上ブロックされたルールをアラートするようにしました。

[time, request_id, action="BLOCK", rule_group_name != "RateLimitRule", ...]
| stats count() as block_count by rule_group_name
| filter block_count > 10

これで、False Positiveを早期に発見できるようになりました。業務影響が最小限で済むんですよね。

OWASPトップ10を意識した設定

OWASP Top 10 2024対策とも関連しますが、WAFのマネージドルールって実は、OWASPで定義されている脆弱性パターンに基づいています。

うちのチームで重視しているのは、以下の3つです。

脆弱性WAFルール対応内容
SQLインジェクションSQLiRuleSetクエリパラメータ検査・エスケープ
クロスサイトスクリプティング (XSS)XSSRuleSetHTMLエンコード検査・サニタイズ
認証回避・ブルートフォースRateLimitRuleIP単位のレート制限・ログ監視

マネージドルールだけで完全には防げないので、カスタムルールと併用する必要があります。

例えば、認証エンドポイント(/api/auth/login)への攻撃対策として、以下のカスタムルールを入れています。1IPあたり100リクエスト/秒を超えたら、ログインエンドポイントをブロックするという仕組みですね。

{
  "Name": "BruteForceProtection",
  "Priority": 3,
  "Statement": {
    "RateBasedStatement": {
      "Limit": 100,
      "AggregateKeyType": "IP",
      "ScopeDownStatement": {
        "ByteMatchStatement": {
          "SearchString": "/api/auth/login",
          "FieldToMatch": {
            "UriPath": {}
          },
          "TextTransformation": "LOWERCASE",
          "PositionalConstraint": "STARTS_WITH"
        }
      }
    }
  },
  "Action": {
    "Block": {}
  }
}

コスト意識の話

最後に、ぶっちゃけ気になるのがコストですよね。AWS WAFって実は、ルール数に応じて課金されます。料金体系をちゃんと把握してないと、予期しない請求が来るんですよ。

  • ウェブACL: 月$5
  • ルール: 1ルールあたり月$1
  • リクエスト: 100万リクエストあたり月$0.60

うちの場合、マネージドルール(3つのグループ)+ カスタムルール(5つ)で、月$13 + リクエスト料金。月間リクエストが500万だと、リクエスト料金だけで$3追加されます。つまり、月$16前後ですね。

Shield Advancedは月$3000なので、中小規模なら「WAF + Shield Standard」で十分かもしれません。ただ、DDoS攻撃を受けた場合のAWS DDoS Cost Protectionが付くのは、Shield Advancedの大きなメリットです。実際の攻撃で追加の通信量が発生しても、AWS側が吸収してくれるんですよ。

今のWAF運用で自動化していること

SCOWL定義や監視系は、できるだけ自動化しています。手作業は必ずミスを招くから、仕組みで防ぐアプローチをとってるんですね。

CloudWatch LogsからAthenaでクエリ — 日次でブロックパターンを分析する。Slack通知 — False Positiveの疑いがあったら即通知する。月1回のルール見直し — ビジネス要件とセキュリティ要件のバランスを確認する。

これらのおかげで、現在のWAF運用はほぼ自動で回るようになりました。人が定期的にチェックするのは月1回の見直しくらいです。

まとめ

正直、WAF・DDoS対策は「入れたら終わり」ではなくて、むしろそこからが始まりなんですよね。

  • AWS WAFは奥が深い — マネージドルールをそのまま使うのではなく、ビジネス要件に合わせた調整が必須。除外ルール設定が本当に重要になってきます
  • False Positiveとの付き合い方 — CloudWatch Logsを活用して、正規ユーザーへの影響を監視。ブロック件数の増減をトレンド化して、異常検知する仕組みが欠かせません
  • 3層防御(CloudFront + WAF + Shield Advanced) — 単一の対策じゃなく、層別に設計することで、初めて実用的なDDoS対策になるんです
  • IPホワイトリストはIaC化 — 手作業は禁止。パラメータストア + 自動テストで、ミスを防止する仕組みが大事
  • レート制限はAPI設計とセット — クライアント側の実装を考慮して、適切な閾値を決める。急激な変更は避ける

次のステップとしては、WAFのログを機械学習で分析して、新種の攻撃パターンを自動検知するような仕組みを検討しています。その実装記録も、また書きたいですね。

U

Untanbaby

ソフトウェアエンジニア|AWS / クラウドアーキテクチャ / DevOps

10年以上のIT実務経験をもとに、現場で使える技術情報を発信しています。 記事の誤りや改善点があればお問い合わせからお気軽にご連絡ください。

関連記事