WAF とアプリ層防御の原理(シグネチャ・正値/負値モデル)
WAF を入れても防げない理由が腑に落ちる。HTTP を正規化して検査するネガティブ/ポジティブ両モデルの原理、エンコーディング回避、誤検知チューニング、RASP との違いを内部動作から解説。
- 1.WAF は HTTP リクエストを正規化したうえで検査するアプリ層フィルタ。ネガティブモデル(既知攻撃のシグネチャを弾く)とポジティブモデル(許可された形だけ通す)の二系統があり、前者は誤検知が少なく未知に弱く、後者は逆の性質を持つ。
- 2.回避の本質は「WAF が見る文字列とアプリが解釈する文字列のズレ」。多重 URL エンコード・大文字小文字・コメント挿入・チャンク分割などで正規化を抜けると、シグネチャに一致しないまま攻撃がアプリに届く。正規化の網羅性が検出力を決める。
- 3.WAF は通信経路上で文字列を見る外付け層で、アプリの内部状態を知らない。RASP はアプリ実行時に SQL 実行や OS コマンドの呼び出し地点で監視するため文脈を持ち、誤検知と回避の両方を構造的に減らせる。両者は競合でなく補完関係。
WAF とは何を検査する層か
WAF(Web Application Firewall)は、HTTP/HTTPS リクエストの中身(メソッド・パス・ヘッダ・クエリ・ボディ)を検査し、攻撃と判断したものをアプリに届く前に遮断するアプリケーション層のフィルタです。L3/L4 のファイアウォールが IP やポートを見るのに対し、WAF は L7 のペイロードを解釈する点が決定的に異なります。
配置はリバースプロキシ形態が一般的で、TLS を終端してから平文の HTTP を検査します。つまり WAF はTLSの内側に立ち、復号済みのリクエストを文字列として観測する外付けの番兵です。アプリのコードには手を入れず、SQL インジェクションやXSS、SSRF といった OWASP Top 10 級の攻撃パターンを経路上で止めるのが役割です。
重要なのは、WAF がアプリの内部状態を一切知らないことです。あるパラメータが最終的に SQL クエリに渡るのか、HTML に出力されるのか、ファイルパスになるのかを WAF は確定できません。だからこそ「文字列が攻撃に見えるかどうか」で判断するしかなく、これが誤検知と回避の根本原因になります。
二つのセキュリティモデル:負値と正値
WAF の判定ロジックは大きく二系統に分かれます。どちらを採るかで誤検知率と未知攻撃への耐性が裏返しになります。
| 観点 | ネガティブ(負値)モデル | ポジティブ(正値)モデル |
|---|---|---|
| 基本方針 | 既知の攻撃パターンに一致したら拒否 | 許可された形だけ通し、それ以外は拒否 |
| 別名 | ブラックリスト型/シグネチャ型 | ホワイトリスト型/許可リスト型 |
| 代表例 | ModSecurity CRS の攻撃ルール群 | 入力スキーマ・正規表現・型・長さの厳格定義 |
| 未知攻撃 | 弱い(ルールに無い亜種は素通り) | 強い(許可形以外は全部落ちる) |
| 誤検知 | 比較的少ない(攻撃の形にだけ反応) | 多い(正規入力も許可定義から外れると拒否) |
| 運用コスト | ルール更新の追従が中心 | アプリ仕様の網羅的な定義が必要で重い |
ネガティブモデルは「攻撃の特徴量」をシグネチャ(正規表現や演算子の組)として持ち、リクエストがそれに一致すれば弾きます。UNION SELECT や ' OR '1'='1、<script>、../ などが典型のパターンです。デプロイが容易で正規トラフィックを邪魔しにくい反面、シグネチャに無い亜種は原理的に検出できません。
ポジティブモデルは逆に「正しい入力の集合」を定義し、その集合 {許可された形} の外を全て拒否します。たとえば「user_id は数字 1〜10 桁のみ」と定めれば、UNION だろうが未知の攻撃だろうが形が合わない時点で落ちます。未知攻撃に強い理想形ですが、全エンドポイントの正規入力を漏れなく定義する必要があり、仕様変更のたびに維持コストがかかります。
実運用の WAF は両者を組み合わせ、シグネチャをベースにしつつ重要パラメータだけ厳格な正値ルールを被せる構成が多くなります。
スコアリングと異常検知
近年の主流ルールセット(OWASP CRS など)は、単一ルールの一致で即遮断する代わりに異常スコア(anomaly scoring)を積み上げます。各ルールに重み(critical=5、warning=3 など)を与え、リクエスト中で一致したルールの合計が閾値以上なら遮断する方式です。
score = 0
各ルール r について:
if r.matches(request):
score += r.severity_weight
if score >= inbound_threshold: # 例: 5
block(request)
この設計は、単独では弱い兆候(怪しい記号が1個ある程度)を1件で誤遮断しないための工夫です。閾値を上げれば誤検知が減るが見逃しが増え、下げればその逆になる。閾値は検出力と可用性のトレードオフを一つのつまみで握るパラメータであり、チューニングの中心になります。
機械学習型は正規トラフィックから統計的ベースラインを学習し、分布から外れたリクエストを異常として扱います。新種を捉えうる一方、学習データに攻撃が混じればそれを正常と学習してしまう毒性に注意が要ります。
正規化と回避:WAF とアプリのズレ
WAF 回避の本質は一言で言える。WAF が検査する文字列と、アプリが最終的に解釈する文字列が一致しないことです。攻撃者はこのズレを突く。
WAF は検査前に**正規化(normalization / canonicalization)**を行います。URL デコード、Unicode 正規化、大文字小文字の畳み込み、連続スペースやコメントの除去などを施し、表記の揺れを潰してからシグネチャに掛けます。ここで処理しきれない変形が回避経路になります。
| 回避手法 | 送られる文字列 | 成立条件 |
|---|---|---|
| 多重 URL エンコード | %2553ELECT(%25→% を再デコード) | WAF が1回しかデコードしないがアプリは多重デコード |
| 大文字小文字混在 | SeLeCt / UnIoN | シグネチャが大小無視せず照合している |
| インラインコメント | UN/**/ION SE/**/LECT | DB はコメントを無視するが WAF が連結を見抜けない |
| Unicode/全角 | ff や全角英字を正規化前に注入 | 正規化テーブルとアプリの解釈が食い違う |
| チャンク/分割転送 | ボディを Transfer-Encoding: chunked で分断 | WAF が再構築せずに部分検査する |
たとえば %2553ELECT は、WAF が1段だけ URL デコードすると %53ELECT のまま(攻撃に見えない)だが、アプリ側がもう一段デコードすると SELECT に化ける。デコード回数の非対称性が突破口です。同様に HTTP リクエストスマグリングは、フロントの WAF とバックのアプリでリクエスト境界の解釈がズレることを悪用します。
検出ロジックがいくら強くても、正規化がアプリの解釈を再現できなければ素通りされます。WAF の検出力は「どれだけ多彩なエンコーディングをアプリと同じ最終形へ畳めるか」でほぼ決まると言ってよいでしょう。新しい文字コードや構文糖が出るたびに正規化の穴が生まれるため、WAF だけを境界防御の本丸に据えるのは危険です。
誤検知とチューニング
ネガティブモデルの宿命が**誤検知(false positive)**です。正規ユーザーの入力がたまたまシグネチャに一致し、正当な操作が遮断される。記事本文に <script> という文字列を含む CMS への投稿、SQL の解説を投稿する技術ブログ、' を含む人名などが典型です。誤検知は可用性を直接損ない、放置すると現場が WAF を無効化する圧力になります。
チューニングの定石は段階的です。
- 検知のみモード(detection-only)で運用し、遮断せず違反だけ記録する。本番トラフィックでどのルールがどれだけ反応するかを実測する。
- 誤検知の多いルールを特定し、例外(exclusion)を定義する。「この URL のこのパラメータではこのルールを無効化」と範囲を絞って除外するのが鉄則で、ルールごと全面オフにすると防御が穴だらけになる。
- 異常スコアの閾値を調整し、可用性と検出力のバランス点を探す。
- ログを継続監視し、アプリ更新のたびに再評価する。
誤検知(false positive=正常を攻撃と誤判定)と見逃し(false negative=攻撃を正常と誤判定)は同時に最小化できません。閾値を厳しくすれば FP が増え、緩めれば FN が増えます。WAF チューニングとは「業務上許容できる FP 率の範囲で FN を最小化する」最適化問題だと整理して覚えるのが要点です。
レート異常への対処は別軸で、攻撃の試行回数そのものを抑えるレート制限と組み合わせると、WAF が個々のペイロードを取りこぼしても総当たりの成立を遅らせられます。
WAF と RASP の違い
WAF の限界(文脈を持てない・正規化のズレで回避される)を構造的に解く別アプローチが **RASP(Runtime Application Self-Protection)**です。
WAF が通信経路上で文字列を外から見るのに対し、RASP はアプリのランタイム内部に埋め込まれ、SQL ドライバの実行直前、OS コマンド呼び出し地点、ファイルオープン地点といった危険な操作の実行点で監視します。決定的な違いは、RASP がそのデータが実際にどう使われるかという文脈を持つことです。
- WAF は「
' OR 1=1という文字列がクエリに見えるか」を推測する。 - RASP は「この入力が SQL 構文を改変したか」を、パース後の実際のクエリ構造を見て判定できる。
このため RASP は正規化のズレに左右されにくく、誤検知も理屈の上では減ります。実際、入力がどんなエンコードで来ても、最終的に SQL 文を構造的に書き換えた瞬間を捉えられる。
| 観点 | WAF | RASP |
|---|---|---|
| 設置場所 | 通信経路上(リバースプロキシ等) | アプリのランタイム内部 |
| 見るもの | 正規化した HTTP 文字列 | 実行点の操作と引数(SQL/コマンド/パス) |
| 文脈 | アプリ内部を知らない | データの用途を実行時に把握 |
| 回避耐性 | 正規化のズレで突破されうる | エンコード差に強い |
| 導入 | アプリ非改変で前段に置ける | 言語ランタイムへの計装が必要 |
| 守備範囲 | Web 入口の全リクエスト | 計装した処理パスに限られる |
ただし RASP は言語ランタイムへの計装(エージェント組み込み)が前提で、対応言語が限られ、性能オーバーヘッドも生じます。WAF は逆にアプリを改変せず前段に置けて広く構えられる。両者は競合ではなく、入口で広く弾く WAF と、実行点で精密に止める RASPという多層防御の異なる層として併用するのが定石です。
まとめ
WAF は HTTP リクエストを正規化して検査するアプリ層フィルタで、既知攻撃を弾くネガティブモデルと許可形だけ通すポジティブモデルを組み合わせ、異常スコアの閾値で遮断します。回避の本質はWAF が見る文字列とアプリが解釈する文字列のズレにあり、検出力は正規化の網羅性でほぼ決まります。誤検知は検知のみモードと範囲を絞った例外で抑えるのが定石で、可用性と検出力のトレードオフをチューニングで握ります。アプリ内部の文脈を持てない WAF の限界は、実行点で監視する RASP が補い、両者は多層防御の異なる層として併用します。WAF は脆弱性そのものを直す代替ではなく、セキュリティヘッダや入力検証と重ねてこそ効果が出る防御層です。
セキュリティ Article
WAF とアプリ層防御の原理(シグネチャ・正値/負値モデル)を実務で読む
TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。
解決すること
WAF
比較で見る軸
難易度: advanced / カテゴリ: セキュリティ / タグ数: 6
導入後に効く点
回避の本質は「WAF が見る文字列とアプリが解釈する文字列のズレ」。多重 URL エンコード・大文字小文字・コメント挿入・チャンク分割などで正規化を抜けると、シグネチャに一致しないまま攻撃がアプリに届く。正規化の網羅性が検出力を決める。
先に潰すリスク
用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。
- 難易度
- advanced
- カテゴリ
- セキュリティ
- タグ数
- 6
判断チェックリスト
- 自社の用途が「WAF / アプリ層防御」に近いか確認する。
- 強みである「WAF は HTTP リクエストを正規化したうえで検査するアプリ層フィルタ。ネガティブモデル(既知攻撃のシグネチャを弾く)とポジティブモデル(許可された形だけ通す)の二系統があり、前者は誤検知が少なく未知に弱く、後者は逆の性質を持つ。」が本当に評価軸になるか確認する。
- 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
- 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
- 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
- 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。