WAFおよび注入の保護
1)なぜAPI時代のWAFなのか
厳密な検証とパラメータ化があっても、注射は次のような原因で起こります:- 統合の「ロングテール」(レガシーコード、アフィリエイトWebフック)、
- 解析の不一致(プロキシ↔フレームワーク)、
- 新しいプロトコル/難読化バイパス技術。
- WAFはコードリリースの前に早期に拒否と「仮想パッチ」境界を与えますが、セキュアな開発は置き換えられません。
2)脅威モデル: APIインジェクションタイプ
SQLi/ORMi: classic/boolean/time-based/stacked;遅延を通して盲目になります。
NoSQLi (Mongo/Elastic):演算子'$ne/$ gt'、 JSONインジェクション、regex-DoS。
コマンドインジェクション/RCE:シェルメタキャラクタ、引数置換、安全でないデシリアライゼーション→コードexec。
XXE: XML内の外部エンティティ/DTD。
SSRF: '169へのアクセス。254.169.254'/内部サービス;DNSリバインド。
テンプレート注入:Jinja/Thymeleaf/Handlebars; '{{77}}'。
LDAP/ELインジェクション、XPath、ヘッダーインジェクション(CRLF)、パストラバーサル。
GraphQL固有:'__ schema'イントロスペクション、クエリの深さ/複雑さ。
JSON/JS固有:プロトタイプ汚染('__ proto __'、 'constructor')。
gRPC/Protobuf:特大メッセージ、スキーマのミスマッチによるフィールド密輸。
3) WAFアーキテクチャ
CDN-WAF周辺:高速ジオフィルタリング/ASN、基本的なボット制御、キャッシュ/アンチパディング。
周囲L7 (NGINX/Envoy/APISIX/Kong):正確な解析、深いルール、PDP/limitsとの統合。
Sidecar/mash (Envoy WASM/Filter):サービスごとに、データに近い、内部APIに対して偽陽性が低い。
推奨事項:デュアルレイヤーモデル(CDN-WAF+L7 WAF)。
4)解析、正規化、アンチバイパス
WAFはアプリケーションと同じ正規表現を見るべきです:- パス正規化('/a/% 2e% 2e/b'→failure)、' UTF-8 '/Unicode confusables、 NULバイト。
- シングルデコード:URL-/HTML-/Unicode-/Base64-layers、ダブルデコードの禁止。
- 制限:'max_headers'、 'max_header_size'、 'max_body_size'、 'max_args'、 JSON depth、 multipart limit、 2x gzip/zip bombs禁止。
- Content-Typeポリシー:'application/json'のみJSONエンドポイント;「polyglot」を拒否します。
5)ルールモデル
負(署名):OWASP CRS (SQLi/XSS/SSRF/Java/Node RCEなど)。クイックスタート。
正(allow-list):厳密なスキーム(JSON スキーマ/Protobuf)、型と範囲;ルート上で。
異常/スコアリング:「疑わしい」兆候の集計→しきい値のブロック。
コンテキスト:'POST/payments'と'GET/status'のプロファイルが異なります。より少ないFP。
6)保護単位(束で)
1.スキーマとタイプ:ビジネスロジックへのJSON スキーマ/Protobuf検証。
2.パラメータ化:準備された式、ORMバインディング、連結禁止。
3.出力エスケープ:HTML/JS/SQLコンテキスト。
4.ボディポリシー:コンテンツタイプ、サイズ、マルチパート制限、JSONハンドルのバイナリの禁止。
5.WAFルール:CRS+カスタム負/正。
6.Rate/Quota/Concurrency: brute/turtle DDoS抑制、protective captchas/challenge for public forms。
7.ネットワーク分離:SSRFの出力ポリシー(RFC1918/metadata/Unixソケットを拒否)。
8.ヘッダー衛生:'X-Content-Type-Options: nosniff'、フロントの'Content-Security-Policy'、 'Referrer-Policy'。
9.GraphQLガード:深さ/複雑さの限界、prod(またはrole-gate)の内省の禁止。
7)構成例
7.1 NGINX+ModSecurity (OWASP CRS)
nginx load_module modules/ngx_http_modsecurity_module.so;
modsecurity on;
modsecurity_rules_file /etc/modsecurity/modsecurity.conf;
modsecurity_rules '
SecRuleEngine On
Подключаем CRS
Include /etc/modsecurity/crs/crs-setup.conf
Include /etc/modsecurity/crs/rules/.conf
Позитивные правила: только JSON и ограничение размера
SecRule REQUEST_HEADERS:Content-Type "!@rx ^application/json($;)" "id:10001,phase:1,deny,status:415,msg:'Only JSON allowed'"
SecRequestBodyLimit 1048576
SecRequestBodyNoFilesLimit 1048576
Блок локальных адресов (SSRF)
SecRule REQUEST_HEADERS:Host "@ipmatch 127.0.0.0/8 10.0.0.0/8 169.254.0.0/16 192.168.0.0/16" \
"id:10002,phase:1,deny,status:403,msg:'Blocked private range'"
';
server {
listen 443 ssl http2;
server_name api.example.com;
client_max_body_size 1m;
proxy_request_buffering on; # защита от slow-POST proxy_read_timeout 300ms;
proxy_connect_timeout 100ms;
location /v1/ {
proxy_pass http://app_backends;
}
}
7.2 Envoy HTTP WAF (WASM+JSONスキーマ+SSRF egress-deny)
yaml http_filters:
- name: envoy.filters.http.wasm typed_config:
config:
vm_config: { vm_id: waf, code: { local: { filename: /plugins/waf.wasm } } }
configuration:
"@type": type.googleapis.com/google.protobuf.Struct value:
crs_profile: "strict"
deny_patterns: ["(?i)union.select", "(?i)(sleep benchmark)\\s\\("]
json_schema:
"/v1/payments:create": "/schemas/payments_create.json"
- name: envoy.filters.http.router
Egress SSRF guard (L4): deny private ranges from gateway filter_chains:
- filters:
- name: envoy.filters.network.tcp_proxy typed_config:
stat_prefix: egress cluster: internet access_log: [...]
tunneling_config:
hostname: "%REQ(:authority)%"
transport_socket:
name: envoy.transport_sockets.tls
7.3 APISIX:タイプ制限と難読化
yaml routes:
- uri: /v1/
plugins:
cors: { allow_origins: "https://app.example.com" }
request-validation:
body_schema:
{"type":"object","properties":{"amount":{"type":"number","minimum":1}},"required":["amount"]}
uri-blocker:
block_rules: ["..","%2e%2e","%2f..","\\x00"] # traversal/NULL proxy-rewrite:
headers:
set:
X-Content-Type-Options: "nosniff"
8)偽陽性(FP)の調整と低減)
ルートごとのプロファイル:適切な場合にのみ厳密なルール(例:'/search'は'/'%'を許可します)。
Shadow/Report-Only:ブロックの前のログレスポンス;メトリクスのA/B比較。
カスタムは「ノイズの多い」正当なパラメータのリストを許可します。
スコアリング:インジケータの合計>しきい値の場合のみブロックします。
実験:新しいルール→自動ロールバックへのトラフィックの少数の割合。
9)観察可能性と発生率
'waf_block_total {rule}'、 'waf_anomaly_score'、 'request_body_rejected_total'、 'schema_violation_total'、 'ssrf_block_total'。
ログ(サンプリング):ルール、リクエストの一部(編集済み)、'trace_id'、 'tenant'、 'route'、 reason。PII/秘密を偽装する。
ダッシュボード:トップルール/パス、FPクラスタ、リリース後のダイナミクス。
インシデント:アーティファクト(ペイロード、必要に応じてpcap)、 RCA製品、および「仮想パッチ」の保存。
10)テストとカオスシナリオ
WAFバイパスエンクロージャ(SQLi/XSS/SSRF)、ダブル/トリプルUnicode混合エンコーディング。
差分の解析:send payload(プロキシとフレームワークが分散できる)(パラメータの重複、配列、';'vs'&')。
Slow-POST/oversize、ジップ爆弾、マルチパーツフォーム、誤った境界。
GraphQL:深さ/複雑さジェネレータ、制限とタイムアウトをチェックします。
11) Antipatterns
「CRSをオンにし、忘れました」:スキームなし、ルートに沿ってチューニングなし。
rawリクエストボディとPIIを使用したログ。
No normalization/size limits→bypasses、 DoS for parsing。
'Content-Type '/charsetチェック→polyglot攻撃をスキップします。
クラウドメタデータへの出力フィルタ→SSRFはありません。
外部APIと内部APIの共通プロファイル。
制御されていない例外「パートナー」→周囲の穴。
12) iGaming/Financeの詳細
支払い/出力ハンドルの強化プロファイル:小規模なボディリミット、厳格なスキーム、アカウント/IBAN/PANフィールドのリストを拒否(マスキング、フォーマットチェック)。
PSP/KYCのWebhook: HMAC署名/相互TLS、個々のWAFプロファイル、アンチリプレイ。
ボット登録やボーナス乱用を防ぐためのGeo/ASNフィルタと行動制限。
インシデントログは、不変(監査)、管轄によって保管されます。
13) Prod Readinessチェックリスト
- 二層WAF (CDN+L7)、単一の正規化とサイズ制限。
- OWASP CRSが有効になっています。書き込みペン上のJSON スキーマ/Protobuf。
- Content-Type/charsetポリシー;double decoding/NULL/traversalを無効にします。
- プライベートバンド/メタデータ用のSSRF-egressブロック。DNSリバインド保護。
- レート/クォータ/パブリックフォームの並行性とアンチボット(チャレンジ)。
- Shadow/Report-Only→canary→enforce;SLOとFPによる自動ロールバック。
- マスキングのメトリック/ログ/トレイル;ダッシュボード「top rules 「/FP。
- バーチャルパッチとRCAプレイブック;定期的なバイパステスト。
- PSP/KYC Webhook、決済ペン、および内部APIのプロファイルを分離します。
14) TL;DR(ドクター)
レイヤーによるビルド保護:正規化と制限→スキーム/タイプ→パラメータ化→WAF (CRS+caste)→rate/botフィルタ→SSRF egressブロック。ルートごとにチューニングし、shadow→canaryで新しいルールを実行し、metrics/FPを監視し、コード修正前に「仮想パッチ」を作成します。支払い/Webhookパスについては、厳格なプロファイル、HMAC/mTLS、最小限の信頼ウィンドウを分離します。