【実務・中級編】SQLインジェクションにおけるブラインドSQLiの検知と悪用 – アプリケーションセキュリティ & 安全な開発防御ガイド

ブラインドSQLi:沈黙するデータベースから「秘密」を吐き出させる技術

現場でインシデントレスポンスを担当していると、よく耳にするセリフがある。「うちのアプリはエラーメッセージを表示しない設定にしているから、SQLインジェクションは起きないはずだ」と。

残念だが、それは大きな誤解だ。データベースが沈黙しているとき、攻撃者は別の言語で語りかけてくる。それが「ブラインドSQLインジェクション(Blind SQLi)」だ。画面にエラーを出さずとも、システムの「反応速度」や「挙動の差異」を観測することで、攻撃者はデータベースの全容を少しずつ、しかし確実に盗み出す。

今回は、この厄介な攻撃の正体と、それを根絶するための実戦的な防御策を解説する。

—

1. なぜ「ブラインド」なのか? 攻撃のメカニズム

ブラインドSQLiには、大きく分けて二つの手法がある。

① 真偽ベース (Boolean-based)

レスポンスの「内容」の変化を見る手法だ。例えば、id=1' AND 1=1-- と id=1' AND 1=2-- を投げ、ページが表示されるか否かで判定する。これだけで「そのSQLは実行可能か?」という命題の真偽が取れる。あとは二分探索を使えば、テーブル名やパスワードのハッシュ値を一文字ずつ抽出できてしまう。

② 時間ベース (Time-based)

レスポンスの「速度」を見る手法だ。エラー画面も真偽の差異も出ない場合、攻撃者は SLEEP() 関数を注入する。
id=1' AND (SELECT IF(SUBSTRING(password,1,1)='a', SLEEP(5), 0) FROM users WHERE id=1)--
もしレスポンスが5秒遅延すれば、パスワードの1文字目は「a」だと断定できる。この泥臭い作業を自動化ツールが数分で完了させるのだ。

—

2. 現場で使える「コピペ厳禁」の防御コード

「パラメーター化クエリ(プリペアドステートメント)」を使えば、SQLiの99%は防げる。しかし、なぜか「クエリの組み立て」を自作するエンジニアが後を絶たない。ここでは、PHPとPythonでの「安全な実装」を提示する。

PHP (PDOによるプリペアドステートメント)

文字列連結でクエリを作るのは今すぐやめよう。

PDO::ERRMODE_EXCEPTION, // エラーを例外として投げる
PDO::ATTR_EMULATE_PREPARES => false, // ネイティブなプリペアドステートメントを強制
]);

$user_id = $_GET[‘id’];

// 安全な実装:プレースホルダを使用
$stmt = $pdo->prepare(“SELECT username FROM users WHERE id = :id”);
$stmt->execute([‘id’ => $user_id]);
$user = $stmt->fetch();

// 結果の表示…
?>

Python (SQLAlchemy/psycopg2)

Python環境でも同様。SQLの構成をDBドライバに委ねるのが鉄則だ。

PostgreSQLでの例
import psycopg2

def get_user_data(user_id):
conn = psycopg2.connect(“dbname=test user=postgres”)
cur = conn.cursor()

# 悪意ある文字列が入力されても、単なる「データ」として扱われる
query = “SELECT username FROM users WHERE id = %s”
cur.execute(query, (user_id,))

return cur.fetchone()

—

3. インフラ層での防御:WAFと設定による多層防御

アプリケーションコードの修正が即座にできない場合、あるいは「防御の厚み」を増したい場合、インフラ層での対策が有効だ。

Nginx/WAFでのフィルタリング

ブラインドSQLi特有のキーワード(SLEEP, BENCHMARK, UNION SELECT 等)を検知するルールをWAF(AWS WAF等)に設定しておくべきだ。

AWS WAF (Managed Ruleの概念設定)

  • SQL Injection Rule: sql_injection_match_set を有効化し、リクエストパラメータを検査する。
  • Rate Limiting: 時間ベースのSQLiは大量のリクエストを伴うことが多いため、同一IPからのリクエストレートを制限する(Rate-based rule)。

—

4. 最後に:エンジニアとして持つべき感覚

ブラインドSQLiを防ぐために最も重要なのは、「自分の書いたコードは、入力値によってどう変貌するか?」を常に疑う姿勢だ。

  • ホワイトリスト検証: IDなら整数か?名前なら許可された文字種か? 入力値チェックは「性善説」で書くな。
  • 権限の最小化: アプリが接続するDBユーザーには、必要最小限の権限(SELECT, INSERTのみ等)しか与えるな。DROP TABLEを許すような権限でアプリケーションを動かすのは、鍵をつけたまま玄関を開けているのと同じだ。

エラーが出ないから安全、ではない。沈黙こそが最大の脅威だと思え。この意識を持つだけで、君たちが作るシステムの堅牢性は劇的に向上するはずだ。

明日からの開発で、一度自分の書いたSQLクエリを見直してみてほしい。文字列連結の + や . が見えたら、それが修正の第一歩だ。

コメント

タイトルとURLをコピーしました