社内のWebアプリに対して脆弱性診断を実施したところ、「Blind SQLインジェクションの可能性あり」という報告が上がってきた。でも実際の画面にはエラーメッセージひとつ出ていない。攻撃されているとしたら、一体どうやって情報が抜かれるんだろう?
こうした疑問を持つエンジニアは少なくありません。通常のSQLインジェクションはデータベースのエラーが画面に表示されることで攻撃の成否が判別できますが、Blind(盲目的)型はアプリが何のエラーも返さない状況でも攻撃者がデータを少しずつ引き出せる点が厄介です。
この記事では、Blind SQLインジェクションの仕組みと主要な攻撃手法(時間ベース・エラーベース)、実際の攻撃の流れ、そして開発者と情シスが今日から取るべき対策を、現場で使えるレベルで解説します。

Blind SQLインジェクションとは?
SQLインジェクションとは、WebアプリケーションのSQL文に悪意ある命令を挿入し、データベースを不正操作する攻撃です。
通常のSQLインジェクションでは、攻撃者はデータベースのエラーメッセージや、クエリ結果が画面に直接表示されることを利用してデータを盗みます。ところが近年のWebアプリは、エラーメッセージを隠したり、クエリ結果を画面に出さない設計にしているケースが増えています。
Blind SQLインジェクションは、その名の通り「目隠しされた状態」でも成立するSQLインジェクションです。攻撃者はデータを直接見ることができない代わりに、アプリの振る舞いの違い(レスポンスの内容の差、応答時間の差)を手がかりにして、1ビットずつデータを推測していきます。
実際の攻撃ではsqlmapのような自動化ツールが使われるため、数千・数万回のリクエストを自動で送り込んでデータを抽出することが可能です。エラーが出ないから安全、という認識は危険です。
Blind SQLインジェクションの主な種類
1. ブール値ベース(Boolean-based Blind)
アプリが「条件が真のとき」と「偽のとき」で異なるレスポンスを返すことを利用します。
例えば「ユーザーが存在する→通常のページ」「存在しない→404ページ」という見た目の違いがあれば、攻撃者は次のようなクエリを仕込みます。
# パスワードの先頭1文字が'M'より大きいか判定する例 ' AND SUBSTRING(password,1,1) > 'M' -- # True(大きい) → 正常ページが表示される # False(小さいか等しい) → エラーページが表示される
これを繰り返すことで、二分探索を使いながらパスワードの全文字を特定できます。1文字につき7回前後の試行で推測できるため、自動ツールを使えば短時間で大量のデータを抽出できます。
2. 時間ベース(Time-based Blind)
アプリのレスポンスが真偽で見た目上変わらない場合でも、応答時間の遅延を使って情報を引き出す手法です。
# MySQL: 条件が真なら5秒待機させる ' AND IF(SUBSTRING(password,1,1)='a', SLEEP(5), 0) -- # PostgreSQL: pg_sleep()を使う例 '; SELECT CASE WHEN (SUBSTRING(password,1,1)='a') THEN pg_sleep(5) ELSE pg_sleep(0) END -- # Microsoft SQL Server: WAITFOR DELAYを使う例 '; IF (SUBSTRING(password,1,1)='a') WAITFOR DELAY '0:0:5' --
攻撃者はリクエストを送って「5秒後に返ってきたか」「即座に返ってきたか」でTrue/Falseを判定します。ネットワーク遅延があっても閾値(例: 3秒以上ならTrue)を設定すれば安定した判定が可能です。
3. アウトオブバンド(Out-of-Band)
アプリのHTTPレスポンスとは別の経路(DNSクエリ・HTTPリクエスト)でデータを外部に送り出す手法です。データベースサーバーが外部に直接通信できる環境でのみ成立します。ファイアウォールで外向き通信を制限している環境では発生しにくいため、3手法の中では最も発生頻度が低いといえます。
実際の攻撃はどのように進むのか
実際の攻撃は次の流れで進みます。
ステップ1:脆弱性の探索
URL・フォーム・クッキーなどのパラメータに '(シングルクォート)や AND 1=1、AND 1=2 を挿入し、レスポンスの変化や応答時間のブレを観察します。
ステップ2:インジェクション可能なパラメータの特定
AND 1=1 のとき正常ページ、AND 1=2 のときエラーページが返れば、ブール値ベースのBlind SQLインジェクションが成立する可能性が高いと判断します。
ステップ3:自動化ツールによる一括抽出
sqlmapなどのツールを使い、データベース名・テーブル名・カラム名・実データを自動抽出します。並列リクエストを活用すれば、数十分以内にパスワードハッシュや個人情報を入手することも可能です。
ステップ4:認証突破・横展開
抽出したパスワードハッシュをクラックし(MD5等の弱いハッシュは即時解読される)、管理者アカウントに不正ログインして内部への横展開へ進みます。
具体的な防御手順
1. プリペアドステートメント(パラメータ化クエリ)の徹底
SQLインジェクション対策の根本はユーザー入力をSQL文から完全に分離することです。プリペアドステートメントを使うことで、入力値がSQL命令として解釈される余地をなくせます。
# PHP (PDO) でのプリペアドステートメント例 $stmt = $pdo->prepare("SELECT * FROM users WHERE id = :id"); $stmt->execute([':id' => $user_id]); # Python (psycopg2 / PostgreSQL) での例 cursor.execute("SELECT * FROM users WHERE id = %s", (user_id,)) # Java (JDBC) での例 PreparedStatement stmt = conn.prepareStatement( "SELECT * FROM users WHERE id = ?"); stmt.setInt(1, userId);
2. ORM・クエリビルダーの正しい利用
フレームワークのORM(Object-Relational Mapping)やクエリビルダーを正しく使えば、プリペアドステートメントが内部的に適用されます。ただし、生のSQLを混在させる(Raw Query)場合は自前でのエスケープが必要です。ORMを使っていても安心せず、動的クエリ部分を重点的にレビューしてください。
3. データベースアカウントの最小権限化
WebアプリがDBにアクセスするためのユーザーには、最低限必要な権限(SELECT・INSERT・UPDATE)のみを付与します。FILE権限・SUPER権限など、Out-of-Band攻撃に悪用されうる権限は剥奪します。
# MySQL: Webアプリ用ユーザーの不要権限を剥奪する例 REVOKE FILE ON *.* FROM 'webappuser'@'localhost'; REVOKE SUPER ON *.* FROM 'webappuser'@'localhost'; GRANT SELECT, INSERT, UPDATE ON myapp.* TO 'webappuser'@'localhost';
4. WAF(Webアプリケーションファイアウォール)の導入
WAFはSQLインジェクションの特徴的なパターン(SLEEP(、BENCHMARK(、AND 1=1 等)を検知してブロックします。ただしWAFは万能ではなく、難読化されたペイロードはすり抜けることがあります。WAFは「第二の防線」として位置づけ、コード側の対策を優先してください。
5. エラーメッセージの非表示化
エラーメッセージを画面に表示しないことは、通常のSQLインジェクションには有効ですが、Blind型には直接効きません。とはいえ、エラー情報は攻撃者への手がかりになるため、本番環境ではエラーをログにのみ記録し、画面には「エラーが発生しました」程度の汎用メッセージのみ表示します。
6. 定期的な脆弱性診断の実施
OWASP ZAP等のスキャンツールやペネトレーションテストでBlind SQLインジェクションが成立しないかを定期的に確認します。Blind型は手動では見つけにくいため、自動化ツールによる検出が特に有効です。また、コードが変更されるたびに脆弱性が混入するリスクがあるため、CI/CDパイプラインへの自動スキャン組み込みも検討してください。
中小企業でも今日からできること
「うちは小さな会社だから関係ない」と思いがちですが、Blind SQLインジェクションは大企業だけが標的ではありません。自動スキャンボットは業種・規模を問わず脆弱なサイトを探し続けています。
・既存コードのSQLクエリ棚卸し: 文字列結合でSQL文を組み立てている箇所を grep -r "SELECT\|INSERT\|UPDATE" --include="*.php" 等で検索し、プリペアドステートメントへの移行を優先する
・フレームワーク・ライブラリのアップデート: ORMやデータアクセス層のライブラリを最新版に保つ
・DBユーザーの権限確認: 今日使っているDBユーザーに不要な権限が付いていないかをチェックする
・WAFの試験導入: クラウドWAF(AWS WAF・Cloudflare等)を試験的に導入し、異常なリクエストのログを確認する
・脆弱性スキャンの定期実施: 開発時だけでなく、本番環境に近いステージング環境でも定期的にスキャンを実行する
よくある誤解と注意点
誤解1: 「エラーが出ないから安全」
Blind SQLインジェクションは、まさにエラーが出ない状況でも成立します。エラーハンドリングの徹底は必要ですが、それだけでは不十分です。
誤解2: 「小さなアプリだから狙われない」
自動化ツールは大量のURLを無差別にスキャンします。SQLインジェクションに脆弱なWebアプリは規模に関係なく発見され、悪用されます。
誤解3: 「WAFがあれば大丈夫」
WAFはBlind SQLインジェクションの検出率が通常型より低い傾向にあります。難読化されたペイロードはWAFをすり抜けることがあるため、コード側の根本対策が必須です。
誤解4: 「外部からのアクセス元IPをブロックすれば防げる」
GeoIPブロッキングや特定IP制限はある程度の効果がありますが、プロキシ・Tor・クラウドIPを経由した攻撃には効きません。コード修正による根本対策が最も確実です。

本記事のまとめ
| 攻撃手法 | 仕組み | 主な対策 |
|---|---|---|
| ブール値ベース | True/FalseのHTTPレスポンスの差で1ビットずつ推測 | プリペアドステートメントで根絶 |
| 時間ベース | SLEEP()等の遅延で応答時間から推測 | プリペアドステートメントで根絶 |
| アウトオブバンド | DNS/HTTPで外部にデータを送出 | DB権限制限+外向き通信の制限 |
Blind SQLインジェクションは「画面に何も出ない」から見えにくいだけで、攻撃者には十分な情報を渡してしまいます。対策の根本はプリペアドステートメントの徹底ですが、DB権限の最小化・WAF・定期スキャンを組み合わせた多層防御を整えることで、リスクを大幅に下げられます。
セキュリティは「見えないから安全」ではなく「正しく知って正しく備える」ことが重要です。
PR
体系的に学ぶ 安全なWebアプリケーションの作り方 第2版(徳丸浩/SBクリエイティブ)
SQLインジェクション・XSS・CSRF等のWebアプリ脆弱性を攻撃と防御の両面から体系的に解説した定番書。Blind型を含むSQLインジェクション対策を深く理解したい開発者・情シスに強くおすすめします。
