PR 本記事には広告(Amazonアソシエイト・もしもアフィリエイト・A8.net等)が含まれます。掲載情報の正確性には努めていますが、商品の詳細は必ずリンク先で最新情報をご確認ください。
CTF・セキュリティ学習

【ブラインドSQLi編】結果が見えなくても起きるSQL注入と守り方|CTF思考フレームワーク #32

かも次郎とアンペンが「ブラインドSQLi」を解説するマスコットイラスト
安全に生きたい編集部

こんにちは、アンペンです!

前回は、ファイルパスの相対解釈を悪用するパストラバーサルを扱いました。

今回は、応答にデータが直接出ないにも関わらず情報が漏れるブラインドSQLインジェクション(Blind SQLi)を見ていきます。1ビットずつ情報を抜く攻撃と、その守り方を学びましょう。

画面にデータが出てないなら、SQLi成立してないんじゃないの?

応答の真偽差や応答時間を使えば、1ビットずつ情報を取り出せる。条件次第ではDBの内容に到達することもあり得るんだ。

SQLインジェクションは有名ですが、今日の『ブラインド』版は、ちょっと不思議な響きですよね。だって、画面にデータが何も表示されないのに、情報が漏れてしまうのですから。「出てないなら安全でしょ?」——そう思いたくなる。でも攻撃者は、答えそのものではなく、サーバの“反応のちょっとした違い”を読み取って、少しずつ秘密を組み立てていきます。今日は『20の質問ゲーム』のたとえで、その地道で確実な手口をほどきます。

まず結論

ブラインドSQLiは、データが応答に出なくても『真偽差(Boolean)』『応答時間(Time)』『外部送信(Out-of-band)』のいずれかで成立します。条件次第ではDBの内容に到達することもあり得るため、守りはパラメータ化クエリ(プリペアドステートメント)とDBユーザの最小権限が中心です。

この記事で分かること

  • 応答に出ないのに情報が漏れる仕組み
  • Boolean / Time / Out-of-band の3型
  • パラメータ化クエリ+最小権限の守り方
難易度:中級向け 所要時間:11分 体験:応答差を意識する おすすめ:#31の後

📖 はじめてのWebセキュリティ #32|ブラインドSQLi編
『応答に出ないSQLi』の手口と、構造的に止める守り方を学びます。 シリーズ一覧を見る →

⚠️ 大事なお約束
この記事の確認は、CTF・自分の検証環境のみで行ってください。本物のサービスに対するSQL文字列のテストは不正アクセスに該当します。

『応答が出なくても漏れる』のはなぜか

通常のSQLiでは、エラーメッセージや結果セットに直接データが出ます。ブラインドSQLiでは、これらが見えない状況でも、サーバの挙動のを利用します。

  • 条件が真のとき: 正常な応答(200, ある画面)
  • 条件が偽のとき: 異常な応答(404, 別画面、無応答)

この『真/偽』が観測できれば、攻撃者は1ビットずつ情報を引き出せます。同様に応答時間の差を観測すれば、SQL内の遅延関数で同じことを行えます(Time-based)。

さらに巧妙なのが、時間を使う方法(Time-based)です。応答の見た目がまったく同じでも、「条件が正しいときだけ、わざと数秒待たせる」ようなSQLを仕込めば、攻撃者は応答の“遅さ”だけで当たり外れを判断できます。画面の表示は何も変わらない。ただ、返ってくるのが速いか遅いか——それだけ。沈黙の中でも“間(ま)”で会話できてしまう、と言うとイメージしやすいかもしれません。

カギは、サーバが見せる“ほんの小さな違い”です。たとえば、ある条件を忍ばせたとき——条件が正しければ画面が正常表示、間違っていればエラー画面、というふうに反応が分かれたとします。これだけで、攻撃者は「今の推測は当たり?外れ?」を知れる。つまり、データそのものが見えなくても、“当たり/外れ”という1ビットの情報さえ漏れれば、そこを足がかりにできてしまうんです。

図解:通常SQLi vs ブラインドSQLi

通常のSQLiは応答にデータが出るが、ブラインドSQLiは真偽差から1ビットずつ情報を取得する比較図
応答が見えなくても、差があれば情報は漏れる。

同じSQLiでも、データが応答に出るかどうかで攻撃のアプローチが大きく変わります。

はい/いいえだけの質問を繰り返して答えに辿り着く20の質問ゲームのたとえ図
『はい/いいえ』だけでも1問ずつ絞れば答えに届く。ブラインドSQLiも同じ構造。
🎯 たとえるなら、20の質問ゲーム

『はい/いいえ』しか答えない相手から、1問ずつ情報を絞り込んでいく『20の質問』は身近なゲームですよね。攻撃者から見たブラインドSQLiは、これをDBに対して大量に行う作業です。1ビットずつでも、何千回も回せばDBのテキストを書き出せます。

ここで覚える用語:プリペアドステートメント
SQL文の構造と『差し込む値』を分離して実行する仕組みです。値部分は『どんな文字を入れても構造を変えない』ため、SQLインジェクション自体が成立しなくなります。Boolean/Time/Out-of-band どの種類のSQLiにも有効な構造的防御です。

ここで強調したいのは、プリペアドステートメントが“ブラインドかどうかに関係なく”効くということ。なぜなら、SQLiの種類(Boolean・Time・Out-of-band)は“どうやって情報を読み取るか”の違いにすぎず、根っこの原因は全部同じ——「SQLの構造に、利用者の値が混ざってしまう」ことだからです。構造と値を最初から分けておけば、混ざりようがない。だから入口を1つ塞ぐだけで、3タイプまとめて消えるんです。

ブラインドSQLiの3タイプ

ブラインドSQLiの“読み取り方”は、大きく3つ。『画面の違いで読む(Boolean)』『応答の速さで読む(Time)』『DBに外部へ通信させて読む(Out-of-band)』。表示・時間・外部通信、と観測する手がかりは違いますが、やっていることは同じ。“何かしらの差”さえあれば、そこから1ビットずつ抜く、という発想です。

代表的なアプローチ

Boolean-based・Time-based・Out-of-bandの3つのブラインドSQLiタイプを示したカード型インフォグラフィック
①Boolean ②Time ③Out-of-band。守りはパラメータ化クエリ+最小権限+外部通信遮断。
  • Boolean-based:条件が真/偽で応答が変わる差を観測する
  • Time-based:条件が真のときだけ遅延(SLEEP等)が走り、応答時間の差で判定する
  • Out-of-band:DBから外部DNS/HTTPに通信を出させ、攻撃者側でその到達を観測する

条件分岐や応答差が見える設計、DBから外向き通信が許される設計は、いずれもブラインドSQLiの足場になります。守る側はこれらをまとめて『応答差・副作用ベース』として認識すると整理しやすくなります。

「1ビットずつなんて、気が遠くなる作業では?」と思うかもしれません。たしかに手作業なら大変です。でも、攻撃者はこれを自動化ツールに任せます。ツールが秒間に何百回も“はい/いいえ”を尋ね続ければ、データベースの中身が数分で書き出されてしまうことも。地道に見えて、機械にやらせれば一瞬。だから「応答差は小さいから大丈夫」という油断は禁物なんです。

練習は、自分のコードで『SQLを文字列連結で組み立てている場所』を探すこと。"... WHERE id = " + 入力 のような書き方が見つかったら、それが要注意ポイントです。攻撃ではなく棚卸し。見つけたら、プリペアドステートメントやORMの安全な書き方に置き換えられるか検討します。本物のサービスに ' OR 1=1 を投げるのは厳禁、自分のラボの中だけで。

CTFでやってみよう:SQL組み立て箇所を点検

やってみよう / 自分の環境・CTFのみ

自分のラボで、SQLを組み立てる箇所を整理しよう

目的は実際に攻撃することではなく、『文字列連結でSQLが組まれていないか』を見つけることです。

  1. コードベースで SELECTINSERT を含むSQL文字列を検索する
  2. 引数を文字列連結で埋め込んでいる箇所と、プリペアドステートメントを使っている箇所を分ける
  3. 連結している箇所は、まずプリペアド・ORMの parameterized API への置換を計画する
  4. DBユーザの権限が最小か(本番テーブルだけアクセス可、テーブル削除等は不可)を確認する
  5. DBサーバが外部通信できない構成かを点検する(Out-of-band防御)
本物のサービスに 'OR 1=1 を投げる試行は絶対にやめてください。確認は自分のラボの範囲だけです。

守りの本命について、よくある疑問に答えておきます。

危ない記号(’ とか)をエスケープすれば、SQLiは防げるんじゃないの?

エスケープも一応の手だけど、抜け道が多いんだ。文字コードの違いや、数値の項目では引用符がそもそも要らなかったり……と、見落としが生まれやすい。だから本命は『プリペアドステートメント』。これは“SQLの文章”と“差し込む値”を最初から別々に扱う仕組みで、値の中に何が書いてあっても、決して命令の一部にはならない。エスケープが“危ない文字を都度なだめる”なら、プリペアドは“そもそも値と命令を混ぜない”。混ぜなければ、注入のしようがないよね。

守る側なら、「パラメータ化+最小権限+外部通信遮断」

SQLiの守りは、種類を問わずパラメータ化クエリが中心です。さらにDBユーザの最小権限化、エラー応答の最小化、DBからの外部通信遮断を組み合わせます。

守るための基本チェック
  • SQLはプリペアドステートメント(またはORMのparameterized API)で組み立てる
  • 文字列連結によるSQL生成を残さない(コードベースから禁止する仕組みを入れる)
  • アプリ用DBユーザは必要なテーブル・操作だけに絞る
  • エラー応答や画面差を最小化し、Boolean/Timeの観測点を減らす
  • DBサーバが外部に出ていく通信を遮断し、Out-of-band経路を塞ぐ
  • WAFと監査ログで異常クエリ・遅延・例外を継続監視する

結局『パラメータ化クエリ』を徹底するのが一番効くんだね。

そう。エスケープに頼るより、SQL構造に値を混ぜない仕組みのほうが構造的に強いんだ。

ここまでをひと言で言うと、SQLiの守りは『SQLの文章と、入れる値を、最初から分けておく』。ブラインドだろうが通常型だろうが、原因は“値が文章に混ざる”の一点。だから、種類ごとに対策を考えるより、プリペアドステートメントで“混ぜない構造”を徹底するのが、いちばんシンプルで確実です。

まとめ:『応答差』があれば情報は漏れる

今回のポイント
  • ブラインドSQLiは応答差・時間差・外部通信で1ビットずつ抜く
  • 3型はBoolean / Time / Out-of-band
  • 守りはパラメータ化+最小権限+外部通信遮断
  • WAF・監査ログで異常パターンを継続検知する

今日の持ち帰りは『応答に出ていなくても、油断しない』です。データが画面に出ないと、つい“漏れていない”と感じます。でも、当たり外れの反応や応答の速さといった“ほんの小さな差”さえあれば、機械は黙々と秘密を組み立てます。だからこそ守りは“出力を隠す”ことではなく、“そもそも注入させない”構造(プリペアドステートメント)に置くのが正解です。

次回は、テンプレートに混ざる悪意でRCEに到達するサーバサイドテンプレートインジェクション(SSTI)を、サーバ側の言語/フレームワーク視点で改めて整理します。

次に読みたい記事

参考資料

記事URLをコピーしました