AWS WAF v2・Shield導入で本番サービスが止まった話|3年の痛い失敗から学んだこと

AWS WAF v2とShield Advancedを導入したら、むしろトラブルが増えた。False Positiveの地獄、ルール設計の落とし穴、実装コストの現実を3年運用の経験から率直に語ります。

本番でWAFが火を噴いた日

先日チームの先輩が「WAF導入してからずっと頭が痛い」と言ってるのを聞いて、自分たちのプロジェクトを思い出した。うちも去年AWS WAF v2とShield Advancedを導入したんだけど、最初の3ヶ月は地獄だった。「これで安心だ」と思ってたのに、むしろセキュリティ対応の仕事が増えたんですよ。

正直、WAFやDDoS対策って「入れたら終わり」だと思ってた。ドキュメント読んで、AWS推奨のルール入れて、みたいなノリで。でも本番で動かしてみたら、想定外の問題ばっかり出てくる。そこで気づいたのが、多くの企業が同じ失敗をしてるってこと。うちが学んだ3年分の知見を共有したいです。

WAF v2導入で最初にぶつかる壁

去年の春、DDoS攻撃のニュースを見て「これはヤバい」と上司と判断して導入を決めた。AWS WAF v2って、CloudFront・ALB・API Gatewayに割り当てられて、マネージドルールも充実してるし、一見すると完璧に見える。

ただ実装してみると、すぐにFalse Positiveが爆発する。マネージドルール(AWS Managed Rules)のCore Rule Setを有効にしたら、正規のユーザーのリクエストまでブロックされ始めた。特にPOSTリクエストが多いエンドポイントはひどくて、JSONボディサイズが大きいだけで403が返ってくるんですよ。

{
  "WebAcl": {
    "Name": "sample-waf",
    "Rules": [
      {
        "Name": "AWSManagedRulesCommonRuleSet",
        "Priority": 0,
        "Statement": {
          "ManagedRuleGroupStatement": {
            "VendorName": "AWS",
            "Name": "AWSManagedRulesCommonRuleSet",
            "ExcludedRules": [
              {
                "Name": "SizeRestrictions_BODY"
              },
              {
                "Name": "GenericRFI_BODY"
              }
            ]
          }
        },
        "OverrideAction": {
          "None": {}
        },
        "VisibilityConfig": {
          "SampledRequestsEnabled": true,
          "CloudWatchMetricsEnabled": true,
          "MetricName": "AWSManagedRulesCommonRuleSetMetric"
        }
      }
    ]
  }
}

このExcludedRulesの設定が本当に鬼門。デフォルトだとめちゃくちゃ厳しいルールが有効になってる。最初は「セキュリティだから仕方ない」と思ってたけど、1週間で1000件近い正規リクエストがブロックされてるログを見た時は本当に焦った。顧客から「APIが返ってこない」と苦情が来ましたから。

そこからが本当の始まりで、False Positiveとの付き合い方をちゃんと学ぶハメになったんですよ。

ルール設計で失敗した話

WAF v2の仕組みって、複数のルールセットを優先順位付けで評価していく。評価がHITしたら、そこで終わり。つまり上から順に厳しいルールを入れるか、許容的なルールを入れるかで全然動きが変わる。

うちの失敗は「とりあえずマネージドルール入れて、あとから調整しよう」と思ってたこと。実際には調整ってめちゃくちゃ大変。なぜなら、本番環境でルール評価のテストができないから。CloudWatch Logsを見て「このリクエストはなぜブロックされたのか」を逆算して確認する。その繰り返しですよ。

SELECT 
  httpRequest.clientIp,
  action,
  terminatingRuleId,
  COUNT(*) as count
FROM waf_logs
WHERE action = 'BLOCK'
  AND timestamp > DATE_FORMAT(current_timestamp - interval '1' day, '%Y-%m-%dT%H:%i:%SZ')
GROUP BY httpRequest.clientIp, action, terminatingRuleId
ORDER BY count DESC
LIMIT 50;

このクエリで、どのルールが何をブロックしてるかを日々確認する。これを3ヶ月続けると、本当に必要なルールと、ノイズルールが見えてくる。

例えば、GraphQLのエンドポイントに対してSQLインジェクション検出ルールはほぼ必要ない。JSONペイロードだから。でも、Query Stringに対する攻撃検出は重要。こういった粒度の調整ができないと、WAFはただのノイズジェネレーターになっちゃう。地味だけど、この調整作業が全てを左右する。

レート制限とボットコントロールの組み合わせ

2026年のWAFで重要なのは、単純なシグネチャベース検出だけじゃなくて、スマートな攻撃検出なんですよね。AWS WAF v2はマネージドルール群の中にBot Control(有料)があって、これが地味だけど強力。

Bot Controlを導入すると、正規ボットと悪意あるボットを自動判別する。GoogleBotとかAmazonBotはスルーして、不正なスクレイピングボットをブロックする。ただし、これ単体では不十分。レート制限と組み合わせないと効かない。

{
  "Name": "RateLimitRule",
  "Priority": 1,
  "Statement": {
    "RateBasedStatement": {
      "Limit": 2000,
      "AggregateKeyType": "IP",
      "ScopeDownStatement": {
        "ByteMatchStatement": {
          "SearchString": "/api/v1/",
          "FieldToMatch": {
            "UriPath": {}
          },
          "TextTransformation": [
            {
              "Priority": 0,
              "Type": "NONE"
            }
          ],
          "PositionalConstraint": "STARTS_WITH"
        }
      }
    }
  },
  "Action": {
    "Block": {
      "CustomResponse": {
        "ResponseCode": 429,
        "CustomResponseBodyKey": "rate-limit-exceeded"
      }
    }
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "RateLimitRuleMetric"
  }
}

でも、ここで落とし穴が出てくる。IP単位のレート制限だと、プロキシ経由のトラフィックでFalse Positiveになるんですよ。例えば、大企業の社員が同じProxy経由でアクセスしてると、1つのIPに見える。すると、正規ユーザーまで制限されちゃう。

うちは学んで、レート制限をIP + APIキーの組み合わせにした。APIキー持ってるユーザーなら多少レートが高くても許容。こういった柔軟性が実装できるかどうかで、WAFの運用が本当に変わるんです。

IP評判リスト・地理的制限との組み合わせ

2026年はIPの評判情報がかなり充実してる。AWS WAF v2はAmazon IP Reputation Listってマネージドルールがあって、既知の悪質なIPアドレスをブロックする。無料だし、これは必須だと思ってる。

{
  "Name": "AWSManagedRulesAmazonIpReputationList",
  "Priority": 2,
  "Statement": {
    "ManagedRuleGroupStatement": {
      "VendorName": "AWS",
      "Name": "AWSManagedRulesAmazonIpReputationList"
    }
  },
  "OverrideAction": {
    "None": {}
  },
  "VisibilityConfig": {
    "SampledRequestsEnabled": true,
    "CloudWatchMetricsEnabled": true,
    "MetricName": "AmazonIpReputationListMetric"
  }
}

組み合わせると効果的なのが、地理的制限。うちのサービスは日本と東南アジアがメインだから、その他の地域からのトラフィックは段階的に絞ってる。ただしブロックじゃなくて、rate limitの強度を上げる工夫をしてる。

理由は単純で、偽陽性が怖いから。正規ユーザーが海外出張してるかもしれない。その時にいきなりブロックされたら、カスタマーサポートが大変ですよ。うちはこうやってリスクレベルで段階的に対応する。完全ブロックじゃなくて、制限の強度を調整する。これが大事。

DDoS対策とWAFの分け方

ここで重要な認識が必要。WAFはアプリケーションレイヤーの攻撃に対抗する。SQLインジェクション、XSS、こういったアプリケーション特有の脅威ですよね。

でも、本当のDDoS攻撃(Layer 3/4レベルの容積型攻撃)はWAFじゃなくてAWS Shield Advancedで対抗する。これらは別物。役割が全く違うんです。

graph TB
    Internet["インターネット"]
    
    subgraph CloudFront層["CloudFront層"]
        Shield["Shield Standard<br/>(自動)"]
        ShieldAdv["Shield Advanced<br/>(有料・DDoS対策)"]
    end
    
    subgraph WAF層["WAF層<br/>(アプリケーション層攻撃対策)"]
        WAF["AWS WAF v2"]
        CoreRules["Core Rule Set"]
        Bot["Bot Control"]
        RateLimit["レート制限"]
        GeoBlock["地理的制限"]
        IPRep["IP評判リスト"]
    end
    
    subgraph アプリケーション層["アプリケーション層"]
        App["API・Web Server"]
    end
    
    Internet --> Shield
    Shield --> ShieldAdv
    ShieldAdv --> WAF
    
    WAF --> CoreRules
    WAF --> Bot
    WAF --> RateLimit
    WAF --> GeoBlock
    WAF --> IPRep
    
    CoreRules --> App
    Bot --> App
    RateLimit --> App
    GeoBlock --> App
    IPRep --> App
    
    style Shield fill:#ff6b6b
    style ShieldAdv fill:#ff3333
    style WAF fill:#4ecdc4
    style CoreRules fill:#95e1d3
    style Bot fill:#ffd93d
    style RateLimit fill:#ffd93d
    style GeoBlock fill:#ffd93d
    style IPRep fill:#ffd93d

Shield Advancedは、大規模なDDoS攻撃(ボットネットからの数Gbpsレベル)に対する保険。通常のWAFでは捌ききれない攻撃に対して、AWSが自動的に検知して対応する。料金は月3000ドル程度だから、決して安くない。でも本当に大事なサービスなら、これは入るべきですよ。

実際、うちも導入前は「LALBで十分」と思ってた。でも去年、実際にDDoS攻撃をくらった。CloudFrontの前段で攻撃が吸収されたおかげで本番は止まらなかったけど、「これがなかったら…」と思ったら、3000ドル/月は安い買い物に思える。

運用の現実的な課題

正直に言うと、WAF v2の運用って思いの外大変。最初はいいんだけど、時間とともに課題が出てくる。

ログの爆発が最大の課題。CloudWatch Logsに毎日数百万件のログが溜まる。こいつを分析しないといけない。Athenaで集計するのはいいけど、コストがバカにならない。うちは月50万円程度AWS Logにかかってて、そのうち半分がWAFログ分析なんですよ。

import boto3
import json
from collections import defaultdict
from datetime import datetime, timedelta

athena = boto3.client('athena')

query = """
SELECT 
  DATE_TRUNC('hour', from_iso8601_timestamp(timestamp)) as hour,
  action,
  COUNT(*) as request_count,
  COUNT(DISTINCT httpRequest.clientIp) as unique_ips
FROM waf_logs
WHERE timestamp > from_iso8601_timestamp(
  DATE_FORMAT(current_timestamp - interval '24' hour, '%Y-%m-%dT%H:%i:%SZ')
)
GROUP BY 1, 2
ORDER BY hour DESC, request_count DESC
"""

response = athena.start_query_execution(
    QueryString=query,
    QueryExecutionContext={'Database': 'waf_logs_db'},
    ResultConfiguration={'OutputLocation': 's3://waf-results-bucket/'}
)

こういったクエリを毎日実行して、アラート対象を特定する。False Positiveが多い時間帯、ルール、ボットの種類。これらを特定して、ホワイトリスト化する。その繰り返しですよ。

ホワイトリストメンテナンスも大変。新機能リリース時、新しいアクセスパターンが出てくる。その度に「このリクエストはなぜブロックされてるのか」を調査して、ホワイトリストに追加する。業務ロジック変更のたびにセキュリティチームを巻き込む手順になっちゃう。

うちはツール化した。自動でFalse Positive候補を検出して、Slackに通知する。セキュリティチームがそれを確認して、「これは正規リクエストだな」と判定したら、ホワイトリスト自動生成。こういった工夫で、運用負荷をかなり軽くできるんです。地味だけど、こういったツール化が運用の質を大きく左右する。

2026年の推奨構成

現在の標準構成を整理すると、こんな感じになってる:

graph TB
    subgraph Internet["インターネット"]
        Users["正規ユーザー"]
        Attackers["攻撃者/スクレイパー"]
    end
    
    subgraph AWSEdge["AWS Edge(CloudFront)"]
        Shield["Shield Standard<br/>自動保護"]
        ShieldAdv["Shield Advanced<br/>DDoS検知・対応"]
    end
    
    subgraph WAFStack["WAF層"]
        WAF["AWS WAF v2"]
        CoreRules["Core Rule Set<br/>SQLi・XSS検出"]
        BotCtrl["Bot Control<br/>ボット判別"]
        RateLimit["Rate Limiting<br/>リクエスト制限"]
        GeoBlock["地理的制限<br/>国別制御"]
        IPRep["IP評判リスト<br/>既知悪質IP"]
    end
    
    subgraph AppStack["アプリケーション層"]
        ALB["ALB/NLB"]
        App["API・Web Server"]
    end
    
    subgraph Monitoring["監視・分析"]
        Logs["CloudWatch Logs"]
        Metrics["CloudWatch Metrics"]
        Athena["Amazon Athena"]
    end
    
    Users --> Shield
    Attackers --> Shield
    Shield --> ShieldAdv
    ShieldAdv --> WAF
    
    WAF --> CoreRules
    WAF --> BotCtrl
    WAF --> RateLimit
    WAF --> GeoBlock
    WAF --> IPRep
    
    CoreRules --> ALB
    BotCtrl --> ALB
    RateLimit --> ALB
    GeoBlock --> ALB
    IPRep --> ALB
    
    ALB --> App
    App --> Logs
    Logs --> Athena
    App --> Metrics
    
    style Shield fill:#ff9900
    style ShieldAdv fill:#ff3333
    style WAF fill:#4ecdc4
    style CoreRules fill:#95e1d3
    style BotCtrl fill:#ffd93d
    style RateLimit fill:#ffd93d
    style GeoBlock fill:#ffd93d
    style IPRep fill:#ffd93d
    style Logs fill:#a8e6cf
    style Athena fill:#a8e6cf

重要なポイントをまとめるとこんな感じ:

  • 段階的防御:Shield(容積型DDoS)→ WAF(アプリ層攻撃)→ アプリケーション層(ビジネスロジック)
  • 複数ルールセット:マネージドルール + Bot Control + カスタムルール + 地理的制限の組み合わせ
  • レート制限:IP単位だけじゃなく、APIキー・ユーザーID単位での柔軟な制限
  • ホワイトリスト管理:自動化ツールで運用負荷を軽減
  • ログ分析:毎日のFalse Positive監視が大事

コスト最適化の工夫

WAFって思いの外お金がかかるんですよ。基本料金に加えて、ルール数、リクエスト数でも料金が変わる。2026年時点では大体こんな感じ:

項目料金摘要
WebACL基本料$5/月必須
ルール$1/ルール/月複雑さで増加
リクエスト$0.60/100万リクエストトラフィック依存
Bot Control$10/月 + $1/100万リクエスト有料機能
Shield Advanced$3000/月DDoS対策

うちのトラフィック規模(月20億リクエスト)だと、WAF v2だけで月15万円。Shield Advancedで月3000ドル。合わせると月60万円近い。これを如何に効率化するかが課題ですよ。

重要なのは、本当に必要なルールだけを有効にすること。デフォルト有効なルールをしっかり精査して、いらないものはオフにする。Bot Controlだって、API層と静的コンテンツでは必要度が違う。

うちがやってるのは、エンドポイント単位でルール構成を変える工夫。静的コンテンツ配信なら最小限のルール。APIなら徹底的にチェック。管理画面なら超厳密に。こんな感じでレイヤー分けしてる:

{
  "LayerConfigurations": [
    {
      "Name": "静的コンテンツ層",
      "Path": "/static/*",
      "EnabledRules": [
        "AWSManagedRulesAmazonIpReputationList",
        "RateLimitHighVolume"
      ],
      "BotControlEnabled": false,
      "EstimatedMonthlyCost": "$8000"
    },
    {
      "Name": "API層",
      "Path": "/api/v1/*",
      "EnabledRules": [
        "AWSManagedRulesCommonRuleSet",
        "AWSManagedRulesSQLiRuleSet",
        "RateLimitAPI",
        "BotControl"
      ],
      "BotControlEnabled": true,
      "EstimatedMonthlyCost": "$35000"
    },
    {
      "Name": "管理画面",
      "Path": "/admin/*",
      "EnabledRules": [
        "StrictRateLimitAdmin",
        "GeoBlockingStrictly",
        "IPWhitelistOnly"
      ],
      "BotControlEnabled": false,
      "EstimatedMonthlyCost": "$5000"
    }
  ]
}

こんな感じでレイヤー分けして、各層に適切なルールを適用する。無駄な検査を減らすことで、コスト30%削減できたんですよ。全部に同じルール適用してるより、段階的に絞る方が結果的に安くなる。

まとめ

WAF v2とShield、3年運用してみて分かった必須ポイントをまとめるとこんな感じです:

運用で大事な3つのこと:

  1. False Positive対策が運用の9割 — マネージドルール入れたら終わり、じゃなくて、CloudWatch Logsとの付き合いが本番の始まり。毎日の監視ツール化が必須ですよ。

  2. レート制限とBot Controlの組み合わせが重要 — 単独では効果薄い。IP単位だけじゃなくて、APIキー・ユーザーID・時間帯など複数軸での制限が大事。リスクレベルで段階的に対応する。

  3. 段階的防御がコスト効率を上げる — 全部に同じルール適用じゃなくて、エンドポイント・レイヤー単位で最小限の検査に絞る。これで効果と効率のバランスが取れる。

次のアクション:

  • CloudWatch Logsの分析クエリを整備して、毎日のFalse Positive監視を自動化する
  • ホワイトリスト管理のツール化検討(自動通知・自動生成)
  • Bot Controlの効果測定を3ヶ月単位で実施して、本当に必要か判断
  • Shield Advancedの導入判断(月10億リクエスト以上なら検討価値あり)

WAFって地味だけど、運用してみると奥が深い。正直まだ完璧な構成には達してないんだけど、この3年で学んだことが少しでも皆さんの参考になったら幸いです。同じような課題で困ってることあれば、ぜひ聞かせてください。

U

Untanbaby

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

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

関連記事