【Azure入門】Application GatewayのWAF入門|OWASP CRS・検出/防御モード・カスタムルール・誤検知チューニングを解説

Azure

「Application GatewayのSKUでWAF_v2を選ぶと守ってくれるらしいけど、そもそもWAFって結局なにをしているの?」

——Application Gatewayの概要をひととおり押さえると、次に立ちはだかるのが WAF(Web Application Firewall=Webアプリケーションファイアウォール) です。名前から「なんとなくセキュリティのやつ」とは分かるのですが、「OWASPって何?」「検出モードと防御モードって?」「誤検知でサイトが止まったらどうするの?」と、一歩踏み込むと途端に手が止まってしまう方が多いところです。

WAFを有効にするのは分かる。でも「いきなり有効にして正常なアクセスまでブロックされたら怖い」って思っちゃうんだよね…

この記事では、Application GatewayのWAFに絞って、次の7つを順番に整理します。読み終えたときに、この7つを自分の言葉で説明できるようになることをゴールにしています。

  • WAFが何か(OWASPルールでL7攻撃を防御)を説明できる
  • 検出モード/防御モード(Detection/Prevention)の違いを理解している
  • マネージドルールセット(OWASP CRS/Bot Manager)を理解している
  • カスタムルール(IP/地理/レート制限)を作成できる
  • 除外(exclusion)と誤検知チューニングができる
  • WAFポリシーをAGW/リスナー/パス単位に関連付けできる
  • WAFログから誤検知を分析しチューニングできる

Application Gatewayそのものの概要(L7ロードバランサーとは何か、SKUの違いなど)は別記事で解説しているので、ここでは「WAFで守るとはどういうことか」という実運用の勘どころに集中します。各セクションの最後に「これが説明できればOK」という自己チェックの目安を置いていますので、学習の到達度確認にも使ってください。

WAFとは?(OWASPルールでL7攻撃を防御する)

WAF(Web Application Firewall)とは、HTTP/HTTPSリクエストの中身(L7)を検査し、SQLインジェクションやクロスサイトスクリプティング(XSS)といったWebアプリ特有の攻撃を、既知の攻撃パターン集(ルール)に照らして検知・ブロックする仕組みです。

ポイントは、WAFが守るのは「アプリケーション層(L7)の攻撃」だという点です。通常のファイアウォールやネットワークセキュリティグループ(NSG)が「どのIP・どのポートを通す/通さない」を制御するのに対し、WAFはリクエストのURL・パラメータ・ヘッダー・ボディの中身まで読んで、「この文字列は攻撃っぽい」と判断します。

身近な例で言うと、空港の手荷物検査 をイメージすると分かりやすいでしょう。ビル入口の警備(NSG)は「入館証を持っているか」を見るだけですが、手荷物検査(WAF)はカバンの中身を開けて、危険物のリスト(ルール)と照らし合わせます。「見た目は普通のリクエストでも、中に危ないものが入っていないか」を確認してくれるわけです。

この「危険物のリスト」にあたるのが、OWASP(オワスプ)の Core Rule Set(CRS) です。OWASPはWebアプリのセキュリティに取り組む非営利団体で、CRSは「よくあるWeb攻撃を検知するためのルール集」です。Application GatewayのWAFは、このCRSをベースに次のような攻撃を検知します。

  • SQLインジェクション(例: 1=1' OR '1'='1 のような不正なSQL断片)
  • クロスサイトスクリプティング(XSS)<script> タグの埋め込みなど)
  • コマンドインジェクション・リモートファイルインクルージョンなどのL7攻撃
  • HTTPプロトコル違反(必須ヘッダーが無い、など)やスキャナー・クローラーの検出

大事なのは、WAFは「バックエンドのコードを一切変えずに」これらの防御を前段で肩代わりしてくれる、という点です。アプリ側の対策が万全でなくても、まず入口で怪しいリクエストを止められる——ここがWAFの価値です。

【ここが説明できればOK①】
「WAFは、HTTP/HTTPSリクエストの中身をL7で検査し、OWASP CRSというルール集をもとにSQLi・XSSなどのWeb攻撃を検知・ブロックする」——これを自分の言葉で言えれば、1つ目のチェックはクリアです。

検出モード/防御モード(Detection/Prevention)の違い

WAFを運用するうえで、最初に理解すべきなのが2つのモードです。WAFポリシーは 検出モード(Detection)防御モード(Prevention) のどちらかで動作します。

  • 検出モード(Detection):怪しいリクエストをログに記録するだけで、実際にはブロックしない。トラフィックはそのままバックエンドへ流れる。
  • 防御モード(Prevention):ルールに一致した攻撃を実際にブロックする。攻撃者には 403 が返り、接続は切断される。ブロックした内容はログにも残る。

例えるなら、検出モードは「試験運転中の手荷物検査」です。「本来ならこの荷物は止めていました」という記録は取るものの、実際には全員そのまま通す。防御モードになって初めて、危険物を持った人を本当に止めるようになります。

ここで、この記事で一番伝えたい実運用の鉄則があります。それは——

「いきなり防御モードにしない。まず検出モードで様子を見て、誤検知を除外チューニングしてから防御モードへ切り替える」

という段階導入の流れです。Microsoftの公式ガイドでも、新しくデプロイしたWAFは本番環境でまず一定期間 検出モードで動かし、ログを集めて例外(除外やカスタムルール)を整えてから防御モードへ移行することが推奨されています。理由はシンプルで、OWASP CRSはデフォルトで「厳しめ」に作られているため、そのまま防御モードにすると正常なリクエストまで誤ってブロック(誤検知)してしまう恐れがあるからです。

「検出モードで下見 → 誤検知を潰す → 防御モードで本番」。この3ステップが黄金ルートだよ!

なお、検出モードで意味のあるログを取るには、WAFの診断ログ(後述)を有効にしておくことが前提です。ログが無いと「様子を見る」材料が集まらないので、モードの話とログの話はセットで押さえておきましょう。

【ここが説明できればOK②】
「検出モードはログのみでブロックしない、防御モードは実際にブロックする。まず検出モードで誤検知を洗い出し、除外チューニング後に防御モードへ段階的に切り替える」——これを説明できれば、2つ目のチェックはクリアです。

マネージドルールセット(OWASP CRS/Bot Manager)を理解する

WAFポリシーの中身は、大きく2種類のルールで構成されます。

  • マネージドルールセット:Azureがあらかじめ用意・メンテナンスしてくれるルール集(自分で書かなくてよい)
  • カスタムルール:自分で作るルール(次のセクションで解説)

このセクションでは前者、マネージドルールセットを見ていきます。Application Gatewayで使える代表的なマネージドルールセットは次の2つです。

OWASP Core Rule Set(CRS)

WAFの本体ともいえる攻撃検知ルール集です。SQLi・XSS・コマンドインジェクションなど、前述のL7攻撃を検知します。Application Gatewayでは CRS 3.2 / 3.1 / 3.0 などのバージョンが選べますが、レート制限などの新機能を使うにはCRS 3.2以降(最新のWAFエンジン)が必要です。特に理由がなければ新しいバージョンを選んでおくのが無難です。

CRS 3.x系で重要なのが「異常スコア(アノマリスコア)モード」という考え方です。これは、1つのルールに一致したら即ブロック、ではなく、一致したルールの深刻度に応じてスコアを加算し、合計が閾値(デフォルト5)以上になったらブロックする方式です。

  • 深刻度 Critical=5 / Error=4 / Warning=3 / Notice=2 を加算
  • 合計スコアが 5以上 になると、防御モードでは 403 でブロック
  • つまり Critical 1つ(=5)だけでブロック、Warning 1つ(=3)だけならブロックされない

この「スコアを合算して判定する」という性質は、後述のログ分析で「なぜブロックされたのか」を読み解くときに効いてきます。まずは「1発アウトではなく、点数の積み上げでブロックが決まる」とだけ覚えておけばOKです。

Bot Manager ルールセット

もう1つのマネージドルールセットが Bot Manager です。こちらは攻撃パターンではなく「ボット(自動化されたアクセス)」を対象にしたルール集で、ボットを次の3カテゴリに分類して、それぞれに対する動作(ブロック/許可/ログ)を設定できます。

  • 悪性ボット(Bad bots):脅威インテリジェンスで既知の悪性IPや、正体を偽るボット
  • 良性ボット(Good bots):Googlebot・BingbotなどのSEOに必要な検索エンジンクローラーなど
  • 不明なボット(Unknown bots):正体が検証できないボット

デフォルトでは「悪性ボットはブロック、検索エンジンなどの良性ボットは許可、不明なボットはログ」といった振り分けになります。これらのマネージドルールセットはWAFポリシーの設定画面で有効化する(オン/オフを切り替える)だけで使え、ルールの中身をAzure側が継続的に更新してくれるのが利点です。

【ここが説明できればOK③】
「OWASP CRSは既知のWeb攻撃を検知する本体ルール、Bot Managerはボットを分類して制御するルールで、どちらもポリシーで有効化するだけで使えるAzure管理のルールセットである」——これを説明できれば、3つ目のチェックはクリアです。

カスタムルール(IP/地理/レート制限)を作成する

マネージドルールセットは「一般的な攻撃」への守りですが、「このIPは拒否したい」「この国からのアクセスだけ許可したい」「短時間に大量アクセスしてくる相手を止めたい」といった、アプリ固有の要件にはカスタムルールで対応します。

ここで大事な前提が1つ。カスタムルールはマネージドルールより先に評価されます。カスタムルールには優先度(priority)という数値があり、小さい数字ほど先に処理されます。あるルールに一致してアクション(許可/ブロック)が確定すると、それより優先度の低いルールは処理されません。この順番の感覚が、カスタムルール設計の肝です。

① 特定IP(範囲)を拒否する

RemoteAddr(送信元IP)を IPMatch 演算子でチェックし、指定した範囲をブロックする例です。以下はAzure PowerShellでの定義イメージです。

$variable = New-AzApplicationGatewayFirewallMatchVariable `
   -VariableName RemoteAddr

$condition = New-AzApplicationGatewayFirewallCondition `
   -MatchVariable $variable `
   -Operator IPMatch `
   -MatchValue "192.168.5.0/24" `
   -NegationCondition $False

$rule = New-AzApplicationGatewayFirewallCustomRule `
   -Name blockBadIP `
   -Priority 10 `
   -RuleType MatchRule `
   -MatchCondition $condition `
   -Action Block `
   -State Enabled
  • -VariableName RemoteAddr:送信元IPアドレスを検査対象にする
  • -Operator IPMatch + -MatchValue "192.168.5.0/24":この範囲に一致したら発動
  • -Action Block:一致したリクエストをブロックする

② 国・地域で制限する(ジオフィルタリング)

GeoMatch 演算子を使うと、送信元IPの地理的な位置(国)で振り分けられます。よくあるのが「日本以外はブロック」のような制限です。ポイントは -NegationCondition $True(否定条件)で「指定した国以外」を表現する点です。

$variable = New-AzApplicationGatewayFirewallMatchVariable `
   -VariableName RemoteAddr

$condition = New-AzApplicationGatewayFirewallCondition `
   -MatchVariable $variable `
   -Operator GeoMatch `
   -MatchValue "JP" `
   -NegationCondition $True

$rule = New-AzApplicationGatewayFirewallCustomRule `
   -Name allowJapanOnly `
   -Priority 20 `
   -RuleType MatchRule `
   -MatchCondition $condition `
   -Action Block `
   -State Enabled

上の例は「送信元が日本(JP)ではないならブロック」=実質「日本からのアクセスだけ許可」という意味になります。NegationCondition(否定)を使いこなせると、この「〜だけ許可」の表現ができるようになります。

③ レート制限(短時間の大量アクセスを止める)

レート制限は、一定時間内のリクエスト数が閾値を超えた相手を止めるルールで、簡易的なDoS対策やアクセス集中対策に使います。カスタムルールの RuleTypeRateLimitRule にして設定します。指定するのは主に次の要素です。

  • 期間(RateLimitDuration):カウントする時間の窓(1分 または 5分
  • 閾値(RateLimitThreshold):その期間内に許可するリクエスト数
  • グループ化(GroupByUserSession):誰単位でカウントするか。ClientAddr(送信元IPごと=既定)/GeoLocation(地域ごと)/None(全体をまとめて)から選ぶ

たとえば「1分間に100リクエストを超えたIPを止める」なら、RateLimitDuration=1分 / RateLimitThreshold=100 / GroupByUserSession=ClientAddr とします。なおレート制限は最新のWAFエンジン(CRS 3.2以降)でのみ利用可能な点に注意してください。

作成したら、まずログ(またはDetectionモード)で意図どおり動くか動作確認してから、本番のブロックに移すのが安全です。IP拒否・国制限・レート制限、いずれも「設定して終わり」ではなく動作確認までがワンセットです。

【ここが説明できればOK④】
「カスタムルールはマネージドルールより先に優先度順で評価され、IPMatchでIP拒否、GeoMatchで国別制限、RateLimitRuleでレート制限を作成できる。作成後は動作確認までセットで行う」——これを説明できれば、4つ目のチェックはクリアです。

除外(exclusion)と誤検知チューニング

WAFを運用すると、必ずと言っていいほど誤検知(フォルスポジティブ)に出会います。誤検知とは、攻撃ではない正常なリクエストを、WAFが攻撃と誤認してブロックしてしまうことです。

典型例が、公式ドキュメントでも挙げられている 1=1 という文字列です。SQLインジェクションでよく使われるパターンなので、CRSはこれを含むリクエストをブロックしがちです。ところが、アプリの正常なフォーム入力にたまたま 1=1 が含まれるようなケースでは、これが誤検知になってしまいます。

こうした誤検知への対処が除外(exclusion)です。除外とは、「リクエストの特定の部分だけをWAFの検査対象から外す」設定です。ルールそのものを無効化するのではなく、「このパラメータの中身は検査しない」とピンポイントで外せるのが利点です。

誤検知チューニングの基本的な流れは次のとおりです。

  1. ログで誤検知を特定する:ブロックされたリクエストのログから、どのルールIDが、どのフィールド(パラメータ)で反応したかを調べる。
  2. 除外を追加する:そのフィールド(例: text1 というリクエスト属性)を除外リストに登録し、そこだけ検査対象から外す。
  3. 正常リクエストが通ることを確認する:同じリクエストを再送し、今度はブロックされずに通ることを確かめる。

ここで注意したいのは、除外にはスコープ(適用範囲)がある点です。除外は「本文(Body)」「ヘッダー(Headers)」「Cookie」といったリクエストの一部分を対象に、条件付きで外せます。認証で使われる __RequestVerificationToken のようなトークンが誤検知の原因になるケースなどが代表例です。

なお、対処にはもう1つ「ルール自体を無効化する」という手段もありますが、これはそのルールをWAF全体で無効にするため守りが弱くなります。「使っていない技術(例: SQLを一切使わないアプリでのSQLiルール)」ならまだしも、基本はルール無効化より、ピンポイントの除外を優先するのが定石です。

【ここが説明できればOK⑤】
「ログから誤検知したルールIDと該当フィールドを特定し、そのフィールドを除外(exclusion)に追加して、正常リクエストが通ることを確認する。ルール全体の無効化より、範囲を絞った除外を優先する」——これを説明できれば、5つ目のチェックはクリアです。

WAFポリシーをAGW/リスナー/パス単位に関連付ける

ここまで見てきたルールや除外は、すべてWAFポリシーという1つの入れ物にまとまっています。そしてこのWAFポリシーは、3つの粒度(スコープ)でApplication Gatewayに関連付けできます。ここがWAF運用の設計上、とても重要なポイントです。

  • AGW全体(グローバル):そのApplication Gatewayを通るすべてのトラフィックに適用
  • リスナー単位(per-site):特定のリスナー=特定サイトにだけ適用
  • パス単位(per-URI):パスベースのルーティングルール単位で、特定のURLパスにだけ適用

関連付けの様子を図にすると、次のイメージです。1つのAGWに複数サイトが同居しているとき、サイトごとに別々のポリシーを当てられます。

          Application Gateway
                  │
   ┌──────────────┼───────────────┐
   │              │               │
【グローバル】   【リスナーA】     【リスナーB】
 既定で全体に    contoso.com       shop.contoso.com
 適用             │                 └ WAFポリシーB(ECサイト用に緩め)
                  ├ /api/*  → WAFポリシーA-1(API用に厳しめ)
                  └ /       → WAFポリシーA(サイトAの既定)

この使い分けが効いてくるのが、まさに前セクションの誤検知チューニングです。「サイトAでは 1=1 を通したいが、サイトBでは通したくない」といったとき、リスナー単位やパス単位のポリシーにすれば、そのサイト/パスにだけ除外やルール変更を適用でき、他のサイトに影響を与えずに済みます。

「全体に効かせる=グローバル」「サイトだけ=リスナー」「特定パスだけ=URI」。粒度を細かくするほど、影響範囲を絞れるんだね!

なお、より細かいスコープのポリシーが優先されるので、「まずグローバルで全体を守り、個別に緩めたい/厳しくしたいサイトやパスだけリスナー単位・パス単位で上書きする」という設計が実務では扱いやすいです。(WAFポリシーの関連付けはWAF_v2 SKUでのみサポートされる点も覚えておきましょう。)

【ここが説明できればOK⑥】
「WAFポリシーはAGW全体(グローバル)/リスナー単位/パス単位の3スコープで関連付けでき、粒度を細かくするほど適用範囲を絞れる。サイトごとに別ポリシーを当てて誤検知対応を局所化できる」——これを説明できれば、6つ目のチェックはクリアです。

WAFログから誤検知を分析しチューニングする(KQL)

最後に、これまでの話を貫く土台——WAFログの分析です。検出モードで様子を見るのも、誤検知の除外先を突き止めるのも、すべてログを読むことが出発点になります。

WAFのファイアウォールログは、診断設定でLog Analyticsワークスペースに送ることで、KQL(Kusto Query Language)でクエリできます。ログは ApplicationGatewayFirewallLog カテゴリに記録されます。まずは「最近ブロック/一致したリクエストを、ルールIDごとに集計する」クエリから始めるのが定番です。

AzureDiagnostics
| where Category == "ApplicationGatewayFirewallLog"
| where TimeGenerated > ago(24h)
| summarize count() by ruleId_s, Message, action_s
| order by count_ desc
  • Category == "ApplicationGatewayFirewallLog":WAF(ファイアウォール)のログだけに絞る
  • summarize count() by ruleId_s ...どのルールIDが何回反応したかを集計する
  • action_sMatched(一致・加点)/Blocked(ブロック確定)などの動作

ログを読むうえでのコツは、前述の異常スコアを思い出すことです。1回のリクエスト(同じ transactionId)で複数のログが出ることがよくあります。実際、公式ドキュメントの例では、1=1 を含むリクエストで次のようなログが並びます。

  • ルール 920350(Host が数値IP)→ action: Matched(Warningで+3)
  • ルール 942130(SQLインジェクション検知)→ action: Matched(+3)。details.data に 1=1 と、反応したフィールド名 text1 が出る
  • ルール 0Inbound Anomaly Score Exceeded)→ action: Blocked合計スコアが5以上になったので最終的にブロックされたことを示す

ここで見るべきは、末尾の Blocked ログ(これは無効化できない必須ルール)ではなく、その手前で Matched になっている実ルール(この例では 942130)です。「どのルールIDが」「どのフィールド(text1)で」反応したか——この2点がログから読み取れれば、前セクションの除外設定にそのまま反映できます。

つまり、この記事全体の実運用フローはこう一本につながります。

検出モードで動かす → WAFログをKQLで分析して誤検知ルールIDとフィールドを特定 → 除外を追加 → 正常リクエストが通ることを確認 → 防御モードへ切り替え

【ここが説明できればOK⑦】
「WAFログを ApplicationGatewayFirewallLog としてKQLでクエリし、Matchedになった実ルールのruleIdと反応フィールドを特定して、除外設定に反映できる」——これを説明できれば、7つ目のチェックはクリアです。

まとめ

今回のポイントを振り返りましょう。この7つを自分の言葉で説明できれば、Application GatewayのWAFはバッチリです。

  • WAF は、リクエストの中身をL7で検査し、OWASP CRS をもとにSQLi・XSSなどのWeb攻撃を検知・ブロックする
  • 検出モードはログのみ、防御モードは実ブロック。検出→除外チューニング→防御の段階導入が鉄則
  • マネージドルールは OWASP CRS(攻撃検知・異常スコア方式)Bot Manager(ボット分類)。有効化するだけで使える
  • カスタムルールはマネージドより先に評価。IPMatch/GeoMatch/RateLimitRule でIP・国・レート制限を作成できる
  • 除外(exclusion)で誤検知フィールドをピンポイントに検査対象から外し、正常リクエストを通す
  • WAFポリシーグローバル/リスナー/パス の3スコープで関連付け。粒度を絞るほど影響範囲を局所化できる
  • WAFログKQL で分析し、Matchedになった実ルールIDと反応フィールドを特定して除外に反映する

WAFは「有効にすれば終わり」ではなく、ログを見ながら誤検知をチューニングして、少しずつ防御を締めていく運用が本質です。まず検出モードで様子を見る——この一歩を踏み出せれば、WAFはもう怖くありません。

あわせて読みたい

  • Application Gatewayとは?L7ロードバランサーの基本(本シリーズの概要編。WAF_v2 SKUの位置づけもこちら)
  • Application Gatewayの作成手順(リスナー・ルール・書き換えの設定)※別記事で解説予定
  • KQL(Kusto Query Language)入門 ※Azure MonitorやLog Analyticsのログ分析の基礎
タイトルとURLをコピーしました