SQLインジェクション防御の深淵:WAFシグネチャの限界と「正規化」の真実
現場でインシデント対応をしていると、いまだに「WAFを入れているからSQLiは大丈夫」という幻想を抱く開発チームに出くわす。だが、断言しよう。WAFは魔法の杖ではない。単なる「入り口の門番」に過ぎない。
今日は、SQLインジェクション(SQLi)をWAFレベルでいかに検知し、かつ誤検知(False Positive)を最小化するのか。その「泥臭い」技術的要諦について、アーキテクトの視点から紐解いていく。
—
1. シグネチャ設計の「死角」を突く攻撃者
多くのWAFはUNION SELECTやDROP TABLEといったキーワードを単純な正規表現で弾いている。だが、攻撃者はその数歩先を行く。
- エンコーディングの多重化:
URL Encode、Hex、Unicode Escape、あるいはデータベース独自の文字変換(MySQLのCHAR()関数など)を組み合わせれば、単純な文字列一致は容易にバイパスされる。 - プロトコルレベルのパケット断片化: WAFがバッファリングする境界を狙った細工や、HTTPヘッダーの意図的な重複によるパーサーの解釈齟齬(HTTP Request Smugglingの応用)により、悪意あるペイロードをWAFの検知対象外へ押し込む手法が存在する。
2. 検知の要:正規化(Normalization)の戦略的実装
シグネチャを当てる前に、入力値を「正規化」しなければ、防御のレイヤーは崩壊する。
正規化とは、攻撃者が隠蔽した「ノイズ」を剥ぎ取り、DBが解釈する直前の「純粋なクエリ」の状態まで還元する工程だ。これを怠れば、防御側は無限のバリエーションを持つ難読化攻撃をすべて網羅しなければならず、結果として膨大な誤検知を招くことになる。
実装のヒント:正規化パイプラインの設計
WAFのエンジンやミドルウェアのフィルタとして実装すべき正規化のフローは以下の通りだ。
1. デコード処理: URLDecode → Unicode/UTF-8 Decode → HTML Entity Decode を再帰的に実行する。
2. コメント削除: SQL内のコメント(--, / ... /)を除去する。
3. 大文字小文字の統一: 全てを小文字に正規化する。
4. 空白の圧縮: 連続するスペースやタブを単一のスペースに変換する。
概念実証:SQLi検知のための簡易正規化パイプライン
import re
from urllib.parse import unquote
def normalize_payload(payload):
# 1. 繰り返しデコードして難読化を解除
decoded = unquote(payload)
# 2. SQLコメントを削除(再帰的に考慮が必要)
no_comments = re.sub(r'(–.)|(/\.\/)’, ”, decoded)
# 3. 空白を圧縮し、特殊文字を正規化
normalized = re.sub(r’\s+’, ‘ ‘, no_comments).lower().strip()
return normalized
検知ロジックへの入力
def detect_sqli(payload):
normalized = normalize_payload(payload)
# 脆弱性のシグネチャ(単純だが強力なパターン)
patterns = [r’union\s+select’, r’order\s+by’, r’sleep\(‘, r’benchmark\(‘]
for p in patterns:
if re.search(p, normalized):
return True # 攻撃と断定
return False
3. 誤検知(False Positive)を殺す「コンテキスト認識」
アーキテクトが最も頭を悩ませるのが、業務ツールで「正規のユーザーが入力した長い文字列」がSQLiとして誤検知されるケースだ。これを防ぐには、シグネチャにコンテキストを持たせる必要がある。
例えば、単純な SELECT という単語はブログの本文にも含まれうる。しかし、SELECT の直後に が続く構造であれば、その確率は飛躍的に高まる。 やカラム名が続き、さらに FROM
- スコアリング方式の採用: 単一のシグネチャで遮断するのではなく、「SQL予約語」「特殊演算子」「DB関数」の出現頻度をスコアリングし、閾値を超えた場合のみ遮断する設計にする。
- ポジティブ・セキュリティモデルの併用: WAFだけでなく、アプリケーション側のプレペアドステートメント(静的プレースホルダ)の利用を強制し、「そもそもSQLiが成立しない構造」を前提とした防御を行う。
4. 次世代への備え:AIプロンプトインジェクションとの共通点
今、LLM(生成AI)の時代において、我々が対峙している「プロンプトインジェクション」は、SQLiの現代版だ。
- 入力のサニタイズ(正規化)
- ガードレイルによる構造化
- 実行環境の分離(サンドボックス化)
これらのアーキテクチャは、SQLiの防御と驚くほど構造が似ている。SQLiで培った「入力値はすべて悪意があるものと見なす(Zero Trust Input)」という哲学は、AI時代のセキュリティアーキテクチャにおいても、何ら色あせることはない。
終わりに:現場のエンジニアへ
セキュリティは、コードを書き、パケットを解析し、攻撃者の脳内をトレースする「終わりのないチェス」だ。WAFの設定画面でルールを追加して満足するな。そのシグネチャが、どのレイヤーで、どのようなパケットの変容を捉えているのか。その「解像度」を上げることこそが、真のセキュリティエンジニアへの道である。
次のインシデントが発生したとき、ログを見て絶望するのではなく、その裏側にある攻撃者の「手癖」を見抜けるようになろう。それが、我々プロフェッショナルの矜持だ。
コメント