【メモ】WAF誤検知検証ツール設計
要旨
WAFの評価は、「攻撃リクエストを何本止められたか」だけでは足りません。運用で本当に痛いのは、ログイン、検索、問い合わせ、API、ファイル添付のような正当リクエストが止まることです。そこで必要になるのが、攻撃テストとは別に、良性トラフィックを体系的に流して誤検知を測る仕組みです。
この記事では、WAF誤検知を評価するためのCLIベース検証ツールの設計を整理します。中心になる考え方は4つです。良性母集団 + 既知FP種 + 良性変異生成 をデータ駆動で回すこと、アップロードを独立カテゴリとして観測すること、current vs candidate の差分比較を前提にすること、そして検証を一度きりの試験ではなく回帰試験に接続することです。1234
なぜ誤検知検証を独立して設計するのか
WAFの誤検知は、単純な「危険な文字列が入っていた」だけで起きるわけではありません。実際には、次のような正当通信が誤って危険扱いされます。
- OIDC/OAuthの
state、nonce、JWT、署名値、相関IDのような長いランダム列 - 自然文検索や問い合わせ本文に含まれる
select、union、scriptといった予約語 - Cookieやヘッダに入る分析タグ、トラッキング値、非標準のUser-Agent
- JSON、GraphQL、XML、SOAPのような構造化リクエスト
multipart/form-dataの boundary、part header、filename、ファイル本文- 大きなペイロード、複数エンコード、Unicode、日本語、絵文字の混在
このため、誤検知検証では「攻撃ペイロードをどれだけ試すか」ではなく、「業務で起こりうる正当通信をどう母集団化するか」が主題になります。Cloudflareが2025年12月5日にWAF最大ペイロード検査サイズの変更で誤検知増加に触れている点や、AWSが誤検知管理を個別に解説している点も、この論点の重さを示しています。21
設計の核は「良性母集団 + 既知FP種 + 良性変異生成」
誤検知ツールの入力データは、次の3層で作ると設計が安定します。
- 良性母集団: ステージング相当の正常操作、匿名化した業務サンプル、仕様書や画面設計から復元した正当フロー
- 既知FP種: 過去障害、運用ログ、例外設定の実績、既知の誤検知事例を模したケース
- 良性変異生成: 正常テンプレートに対し、長さ、文字種、エンコード、配置、構造、ファイル種別を変える派生生成
ケースのクラスは、少なくとも benign、known_fp_seed、generated_benign_variant、malicious を持たせます。このうち主評価対象は、benign、known_fp_seed、generated_benign_variant が block、deny、challenge されたケースです。count は即FPとは限りませんが、将来の運用負荷や例外設定候補を見つけるための重要な観測対象になります。15
10,000件計画は総数より層別が重要
誤検知検証では、10,000件という総数そのものより、どの入力面をどう配分するかが重要です。実務寄りの配分例は次のとおりです。
| カテゴリ | 件数 | 狙い |
|---|---|---|
| auth | 1200 | ログイン、OIDC/OAuth/SAML、MFA、パスワード再設定の誤検知を見る |
| search | 900 | 自然文検索、引用符、検索演算子、フィルタ条件を試す |
| free_text | 1400 | 問い合わせ、コメント、申請理由、コードやログ貼り付けを含める |
| api_json | 1600 | REST/JSON、配列、ネスト、GraphQL、XML系の構造を広く見る |
| cookie_header | 900 | Cookie、UA、相関ID、分析メタデータ由来の誤検知を切り出す |
| admin_business | 700 | CSV取込、一括更新、業務パラメータ密度の高い通信を扱う |
| mobile_bot_like | 800 | モバイルアプリ、SDK、監視、非ブラウザ通信を分けて評価する |
| upload_text | 900 | txt、csv、json、xml、html、logの添付本文を観測する |
| upload_binary | 900 | pdf、docx、xlsx、jpg、png、zipなどのバイナリ添付を観測する |
| upload_multipart_structure | 700 | boundary、part header、複数ファイル、サイズ閾値近辺を観測する |
| 合計 | 10000 | 総量よりも、偏りを抑えた層別サンプリングを優先する |
auth、api_json、free_text を厚めにし、アップロード関連を合計2,500件にするのは妥当です。実務上はこの3領域で事故の影響が大きく、かつファイル添付は本文、filename、multipart構造、サイズ制御の複合面になるからです。62
アップロードを独立カテゴリとして扱う
ファイル添付は「本文の一種」として雑に扱うと、誤検知の原因分析が壊れます。最低でも次の観測面を独立して持つべきです。
- filename
- multipart boundary
- part header
- declared content-type
- file body bytes/text
- total request size
- multi-file composition
- metadata fieldとの組み合わせ
たとえば、error_select_union_log.txt のようなファイル名が引っかかったのか、ログ本文に含まれるSQL風文字列が引っかかったのか、boundaryやpart headerが怪しく見えたのかでは、調整すべきルールが変わります。ここを分けないと、「アップロードが止まった」という粗い知見しか残りません。24
FPパターンを先に持つと、調整の会話が速くなる
誤検知をただ一覧化するだけでは、調整や例外化の議論が進みません。ケースには fp_pattern_code を付け、少なくとも次のような観点で整理しておくと扱いやすくなります。
| コード | 名称 | 典型例 |
|---|---|---|
| FP-001 | token_like_random_string | JWT、state、nonce、署名値、相関ID |
| FP-002 | natural_text_keyword_collision | 自然文中の select、union、script |
| FP-005 | json_xml_graph_structure | ネストJSON、GraphQL、XML断片 |
| FP-007 | non_browser_user_agent | モバイルアプリ、SDK、監視ツールのUA |
| FP-009 | multipart_boundary_header_anomaly | boundaryやpart headerの複雑性 |
| FP-010 | upload_text_keyword_collision | 添付されたログやコード例の文字列衝突 |
| FP-013 | large_payload_inspection_limit | 検査サイズ閾値近辺での挙動変化 |
| FP-017 | multi_part_correlation_effect | 複数partの組み合わせによる誤判定 |
実装上は、category、subcategory、business_flow、input_surface、suspected_signal_field、rationale とあわせて記録しておくと、後で「どの機能の、どの入力面が、どの種の誤検知を踏んだのか」を追いやすくなります。14
CLIツールとしてどう切るか
ケース定義はYAMLで持ち、テンプレートからHTTPリクエストを組み立てる形が扱いやすいです。最小イメージは次のようになります。
id: BENIGN-AUTH-000001
class: benign
category: auth
subcategory: oidc_callback
business_flow: user_login
input_surface:
- query
- cookie
- header
method: GET
path_template: /oauth/callback
query_params:
code: "{{oauth_code}}"
state: "{{state_token}}"
headers:
User-Agent: "{{user_agent_mobile_or_browser}}"
X-Request-ID: "{{request_id}}"
cookies:
session_trace: "{{trace_cookie}}"
expected_waf_action: allow
fp_pattern_code:
- FP-001
- FP-007
この上で、責務は次のように分割すると実装しやすくなります。
case_loader.py: YAMLケースを読み込み、スキーマ検証と正規化を行うrequest_builder.py: query、header、cookie、bodyをHTTPリクエストへ組み立てるmutation_engine.py: 長さ、文字種、エンコード、配置、構造の変異を生成するupload_builder.py:multipart/form-dataとファイルpartを構築するexecutor.py: 並列送信、タイムアウト、リトライ、RPS制御、dry-runを扱うresponse_normalizer.py: 製品差を吸収し、waf_action、matched_rule_ids、matched_labelsを標準化するclassifier.py:allow、count、blockと期待値から FP/TN などを判定するreporter.py: CSV、JSON、Markdownの集計レポートを出すdiff_runner.py:currentとcandidateの差分を比較する
ここで重要なのは、送信器よりも先に「ケースデータモデル」と「結果の正規化モデル」を固めることです。これが曖昧だと、ベンダーごとの差を吸収できず、差分比較や回帰試験へつながりません。35
実行モードと集計軸を最初から決めておく
実行モードは、少なくとも次を持っておくと運用しやすくなります。
smoke: 100件前後で疎通とスキーマを確認するpilot: 1,000件前後で偏り、送信失敗、ログ突合、WAF観測項目を確認するfull: 10,000件規模で本番相当の誤検知傾向を取るdiff: 現行ルールと候補ルールの差分を見るfocused:category、fp_pattern_code、file_type、tag単位で絞り込むreplay: 失敗ケースや要再検証ケースを再送する
集計軸は、category別FP率、subcategory別FP率、matched_rule_id別件数、field/location別件数、file_type別件数、request_size_tier別件数、current vs candidate 差分件数 を最低限持たせるべきです。誤検知率だけを一枚で出すより、「どの業務フローが壊れるか」を前面に出したほうが、WAF担当とアプリ担当の会話が進みます。17
準備から回帰試験までの運用
この種のツールは、実装よりも準備の質で結果が変わります。最低限、次の順序で進めるのが無難です。
- 主要業務フローと入力面を棚卸しする
- WAFログから
rule_id、label、action、matched fieldを取得できることを確認する - 匿名化済みの良性テンプレートと既知FP seedを用意する
- まず
pilotを回し、重複、偏り、送信失敗、タイムアウト、ケースID突合を確認する - その後
fullとdiffを回し、例外化候補、scope-down候補、Count移行候補を整理する - WAFルール更新時、アプリ変更時、upload仕様変更時に回帰試験として再実行する
Detection as Codeの文脈では、ルール更新のたびに同じ母集団で比較できることが価値になります。単発の検証イベントではなく、「ルールを変えたら必ず差分を見る」運用に接続して初めて効きます。37
安全要件は最初から仕様に埋め込む
誤検知検証ツールは、防御検証のつもりで作っても、作り方を誤ると攻撃補助に寄りやすい領域です。したがって、仕様段階で次を明示しておくべきです。
- 本番環境へ直接流さない
- 実データをそのまま使わず、匿名化または擬似化する
- 送信先、並列度、RPS上限を明示制御する
- ランダム生成でも
seed指定で再現できるようにする - 攻撃強化や侵害目的に使える機能は追加しない
この制約を先に入れておくと、ツールの責務が「WAF誤検知の評価と回帰」に固定され、実装もぶれにくくなります。
まとめ
WAFの誤検知検証で大事なのは、攻撃テストを増やすことではなく、正当通信の設計密度を上げることです。特に、良性母集団 + 既知FP種 + 良性変異生成 の3層、アップロードの独立観測、current vs candidate の差分比較、ログ起点の分析は外しにくい軸です。
CLIベースの検証ツールとして作るなら、YAMLケース、変異生成、multipart構築、結果正規化、FP分類、差分比較、CSV/JSON/Markdown出力までを一つの流れで設計しておくと、10,000件規模の検証から継続回帰までつなげやすくなります。WAFでは「FP率」そのものより、「どの機能が壊れるか」が最重要です。この視点を最初からデータモデルに埋め込むのが、実務ではいちばん効きます。