ブラインドSQLi:沈黙のデータ流出をどう防ぐか?現場で使える「無効化」の鉄則
現場のエンジニア諸君、お疲れ様。今日もどこかのWebアプリが、たった一行の「不用意なクエリ」でデータベースを丸裸にされている。
SQLインジェクション(SQLi)というと、エラーメッセージが画面にドバっと出るような派手な攻撃を想像するかもしれない。だが、プロの攻撃者はそんな野暮なことはしない。彼らが好むのは「ブラインドSQLi」だ。画面に何も出ない、エラーも吐かない。ただ、レスポンスが「0.1秒遅い」か「正常か」というだけの差から、DBの中身を1ビットずつ盗み出す。
今日は、なぜこれが怖いのか、そしてどうすれば「コードレベル」で完全に封じ込められるのかを、実戦的な視点で叩き込む。
—
1. なぜブラインドSQLiは「最強のステルス」なのか
一般的なSQLiはエラーを表示させるが、近年のセキュアな設定ではエラー表示がオフになっていることが多い。そこで攻撃者は、DBの論理状態を操作する。
時間差攻撃(Time-based)のロジック
例えば、ユーザーID 1 を取得するクエリに対し、以下のようなペイロードを送り込む。
' AND (SELECT 1 FROM (SELECT(SLEEP(5)))a WHERE (SELECT SUBSTRING(password,1,1) FROM users WHERE id=1)='a')--
もしパスワードの1文字目が ‘a’ なら、サーバーは5秒間応答を止める。攻撃者はこの「レスポンスの遅延」というたった一つの事実から、パスワードを総当たりで特定するわけだ。ログには「正常なアクセス」として残るため、IPSや簡易的なWAFをすり抜けることも珍しくない。
—
2. 現場でやるべき「完全防御」のコード実装
防御の鉄則は一つ。「SQL文の構築を、自分で行わないこと」だ。これに尽きる。
不適切な実装(絶対にやってはいけない例:PHP)
// ユーザー入力をそのまま文字列結合している最悪の例
$id = $_GET['id'];
$sql = "SELECT * FROM users WHERE id = " . $id;
$result = $db->query($sql);
これだと、1 OR 1=1 と打ち込まれた瞬間にDBが全開になる。
セキュアな実装(PDOによるプリペアドステートメント)
プリペアドステートメントは、SQLの「構造」と「データ」を分離する。DBエンジンは、入力値を実行コードとして解釈できなくなる。
// PDOを用いたセキュアな実装
$id = $_GET['id'];
// プレースホルダー (?) を使用
$stmt = $pdo->prepare("SELECT * FROM users WHERE id = ?");
// 実行時に値をバインドする(ここでデータ型も固定される)
$stmt->execute([$id]);
$user = $stmt->fetch();
これだけで、たとえ攻撃者がどんなに巧妙なSQL文を注入しようとしても、DBはそれを「単なる文字列」として扱う。攻撃は不発に終わる。
—
3. アプリ層以外での防御:WAFと設定の多重防護
コードレベルの修正が大前提だが、万が一の漏れに備えて「外壁」を固めることも重要だ。
Nginx/ModSecurityによるリクエスト制限
WAF(ModSecurity等)を入れているなら、SQLのキーワードをパターンマッチングで弾く設定を入れる。ただし、これだけで安心は禁物だ。あくまで補助である。
# Nginx設定例:基本的なSQLiパターンのブロック(正規表現)
# 実際にはModSecurityのルールセットを適用するのがベスト
location / {
if ($query_string ~* "union.*select.*\(") {
return 403;
}
if ($query_string ~* "sleep\(.*\)") {
return 403;
}
}
最小権限の原則(重要!)
DBユーザーの権限管理は徹底しているか? Webアプリが使うDBユーザーは、DROP TABLE や GRANT 権限を持っていてはならない。
SELECT,INSERT,UPDATEのみ許可する- システムテーブル(
information_schema等)へのアクセスを制限する
これだけで、万が一SQLiを許してしまったとしても、被害を「データの抽出」のみに限定できる。
—
4. 最後に:エンジニアとしてのマインドセット
いいか、セキュリティ対策に「銀の弾丸」はない。コードレビューの際、$sql = "..." という文字列結合を見かけたら、それがどんなに小さそうな修正であっても「爆弾」だと思って止めること。
ブラインドSQLiは、あなたのシステムの「沈黙」を悪用する。だからこそ、開発者が積極的に「不正な入力を拒絶し、ログを監視し、権限を絞る」という意識を持たなければならない。
現場でコードを叩く諸君、今日からさっそく自プロジェクトのDBクエリを確認してくれ。もし文字列結合を見つけたら、それが今日、君が修正すべき最大の脆弱性だ。
健闘を祈る。
コメント