SQLインジェクションという「古き亡霊」の現在地:WAFとログ分析の深淵
SQLインジェクション(SQLi)は、Webセキュリティの歴史そのものだ。しかし、現代のアーキテクトにとって、これは単なる「古い脆弱性」ではない。生成AIによる自動生成コードが爆発的に増え、ORM(Object-Relational Mapping)の抽象化レイヤーの背後で、開発者が意図しないクエリが生成される時代において、SQLiは「予測不能な変異体」として蘇っている。
今日は、教科書的な「入力値をサニタイズせよ」という勧告の先、すなわち、WAFのシグネチャを突き抜け、アプリケーションのログから異変を嗅ぎ分けるための、泥臭くも高次元な防衛論を語ろう。
—
1. WAFシグネチャの限界と「文脈」の解釈
多くのエンジニアは、WAFに UNION SELECT や -- といったキーワードを登録して満足する。だが、攻撃者は常にその上を行く。URLエンコード、二重エンコード、あるいはデータベース特有のコメントアウト記法(/!50000SELECT/)を駆使し、シグネチャの網の目を潜り抜ける。
シグネチャ設計の盲点
WAFを「文字列マッチング」のツールと捉えてはならない。それは「プロトコル解析器」であるべきだ。
- 正規化(Normalization)の不備: 攻撃者は
U%4eION SEL%45CTのように一部をエンコードしてくる。WAF側でこれらを適切に正規化し、ツリー構造として評価できなければ、防御は脆くも崩れる。 - WAFのチューニング設定例 (ModSecurity/OWASP CRSの思想):
単なるキーワード検知ではなく、演算子と命令の組み合わせを異常値とみなす
例: 結合演算子とコメントの同時出現をスコア化する
SecRule ARGS “@rx (?i)(union|select|insert|delete|update).(–|#|/\)” \
“id:10001,phase:2,log,deny,status:403,msg:’SQLi Pattern Detected – Advanced Context'”
ここで重要なのは「拒否」だけではなく、その後の「相関分析」へログを飛ばすことだ
—
2. アプリケーションログにおける「異常クエリ」の検知
WAFをすり抜けた攻撃は、データベースのクエリログで捕らえるしかない。しかし、膨大なクエリの中からどうやって攻撃を見つけるか? ここで鍵となるのは、「クエリの構造的類似性」の分析だ。
異常検知のアーキテクチャ
正常なアプリケーションは、特定のルーチンから似たような構造のクエリを発行する。SQLi攻撃者は、これまでのクエリ構造を破壊し、自身の意図した UNION や JOIN を挿入する。
- クエリのフィンガープリント化:
クエリからリテラル値(IDや名前など)を排除し、構造(SQLの構文木)だけをハッシュ化する。
SELECT FROM users WHERE id = 10 → SELECT FROM users WHERE id = ?
この「ハッシュ値」が平常時と異なるクエリが発生した場合、それをインシデントトリガーとしてSIEMに投げるのだ。
Pythonによるクエリ構造解析のプロトタイプ
import sqlparse
def get_query_structure(query):
# SQLパーサーを使用してクエリをトークン化し、構造のみを抽出
parsed = sqlparse.parse(query)[0]
tokens = [token.ttype for token in parsed.flatten() if not token.is_whitespace]
# トークンの並びをハッシュ化して「構造」を定義する
return hash(tuple(tokens))
運用時のログ監視ロジック例
current_structure = get_query_structure(incoming_query)
if current_structure not in KNOWN_SAFE_STRUCTURES:
alert_security_team(f”Unknown query structure detected: {incoming_query}”)
—
3. 生成AI時代のガードレイル:LLMへのプロンプトインジェクションとSQLi
今、我々が直面している最大の脅威は、ユーザーがAIチャットボットに投げたプロンプトが、バックエンドで直接DBクエリを生成するアーキテクチャだ。
生成AIに対して「ユーザーの入力をSQLクエリに含めるな」と指示するだけでは不十分だ。「中間の検証層(ガードレイル)」を物理的に分離しなければならない。
1. パラメーター化クエリの強制: LLMが出力したSQLを直接実行せず、必ずDBドライバのプリペアードステートメントを経由させるラッパーを挟む。
2. 実行権限の最小化: AIがアクセスするDBユーザーには、SELECTのみ、かつ特定のビューのみを許可する。DROPやGRANTはもちろん、INFORMATION_SCHEMAへのアクセスすら制限すべきだ。
—
最後に:防御は「技術」ではなく「観測」である
SQLインジェクションは、アプリケーションのコードを1行直して終わるものではない。それは、通信パケットの解析からデータベースのクエリログ分析まで、スタック全体を貫く「観測」のプロセスだ。
もしあなたがアーキテクトなら、自分のシステムが「どのようなクエリ構造を正当としているか」を定義することから始めてほしい。攻撃者の思考をトレースするのではなく、自分のシステムの「正常な挙動」を厳密に定義する。これが、現代の最高峰のセキュリティ防衛術であると、私は確信している。
セキュリティとは、完璧を求めることではない。攻撃者が「このシステムを攻略するのはコストに見合わない」と判断させるだけの、見えない壁を積み上げ続けることなのだ。
コメント