何が起きるか

SQLインジェクションは、アプリが利用者の入力をSQLへ直接連結し、入力に含まれる記号や式までDBの命令として実行してしまう問題です。検索欄やURLのIDに入れた文字が、比較する値の範囲を抜けて問い合わせの条件を変えます。

説明用に、注文検索APIが受け取った名前を引用符で囲み、WHERE name = '入力値'という条件へ連結する場合を考えます。たとえば入力がx' OR '1'='1なら、SQLの条件はWHERE name = 'x' OR '1'='1'になります。入力中の引用符が名前の値を終わらせ、ORの後の'1'='1'はどの行でも真なので、名前がxでない注文も検索結果に入ります。アプリが結果をHTTPレスポンスへ入れれば、攻撃者は他人の配送先を自分の端末で読めます。DB接続ユーザーがそのデータを読め、結果が返ることが条件です。

正常ならどう動くか

名前検索では、サーバーがWHERE name = ?のようなSQLの形を固定し、?へ渡す名前はドライバのバインドAPIで別の値として指定します。DBはSQLの構造と値を区別するため、名前に引用符が含まれても名前の一部として比較し、新しい条件式にはしません。 ドライバはアプリがDBへ問い合わせるためのライブラリです。たとえばquery("SELECT * FROM orders WHERE name = ?", [name])のようにSQLと値を別の引数へ渡します。バインドAPIがDBの仕組みに合わせて値として受け渡すため、アプリが文字列の?を入力そのものへ置換する方法とは異なります。

注文を本人だけに返す場合は、ログイン利用者のIDを使う所有者の条件も別に設定します。バインドはSQL構文の改変を防ぎますが、誰の注文を返してよいかという認可まで自動で追加するものではありません。

不備と攻撃が成立する流れ

攻撃者がSQLの引用符や条件式として意味を持つ文字を検索欄へ入力します。APIが入力をSQLへ文字列連結すると、DBがその一部を値の終わりや別の条件と解釈し、予定していないレコードを検索します。HTTP入力をSQLの構造として実行してしまう処理が不備です。

DB操作のコードからSQLを生成するORMを使っていても、開発者がSQLを直接書くrawクエリへ入力を連結すれば、同じ注入が起きます。利用者がプロフィールなどへ保存させた値を、後日の集計SQLへ連結する場合もあります。保存した場所がDB内であっても、その文字列の作成者は利用者なので、安全なSQL構文として扱う理由にはなりません。

起こり得る被害と条件

読み取りの例では、攻撃者が検索範囲を広げ、他人の注文や連絡先を取得する問い合わせを実行させます。アプリが結果をHTTPレスポンスへ含めると、攻撃者はその情報を自分の端末で入手できます。

更新の例では、更新対象を決める条件や保存する値を変え、他人の配送先や権限情報を書き換えるおそれがあります。アプリがDBへ接続するユーザーに、そのテーブルの更新権限があることも必要です。

結果の文字列を返さないAPIでも、攻撃者が注入できる条件に「DB内の秘密の先頭文字がAなら待機し、違えばすぐ終わる」という処理を組み込める場合があります。アプリがDBの処理終了を待ってHTTP応答を返す構成なら、攻撃者は入力の候補文字を変えて応答時間を比較し、秘密の文字を推定できます。条件付きの待機に使えるDB機能、秘密の読み取り権限、時間差を観測できることが追加条件です。

OSの命令実行まで可能かは、DBが公開する機能や接続ユーザーの権限・設定に依存します。SQLの注入を確認しただけで、サーバー全体の権限を取得したと扱ってはいけません。

防ぎ方と、防げる理由

下の説明用コードでは、IDをSQL文字列の後ろへ連結する処理をやめ、ドライバのバインドAPIへ渡しています。validatedIdは型と範囲を検査したIDで、?はその値を入れる場所です。ドライバが値をSQLの構文と分離するため、入力を新しい条件として解釈させずに比較できます。

列名や並び順は、通常の値のようにバインドできない場所です。利用者が「名前順」を選んだらサーバー内の固定したname列へ対応させるなど、選択肢を固定します。DB接続ユーザーにも必要な読み取り・更新だけを許可すると、別の不備でSQLが変更されても、権限のないテーブルの取得や更新をDBが拒否できます。

説明用の擬似コード(実際の製品コードではありません)

// 危険
query("SELECT * FROM tools WHERE id=" + request.id);
// 修正: ドライバのバインドAPIを使用
query("SELECT * FROM tools WHERE id = ?", [validatedId]);

よくある誤解

エラーメッセージを隠しても、不正なSQLが動く原因は残ります。引用符を独自に置換するだけの処理も、文字コードや埋め込み先を誤ると安全性を保証できません。

実装の確認点

query、raw、SQL文字列のテンプレートを探し、HTTPのID・検索語や、利用者が保存した値がどこから入るか確認します。値にはバインドを使い、動的な列名・ソート指定には固定候補を使っているか調べます。問い合わせ結果、エラー、時間差など、情報が攻撃者へ届く経路も確認します。

出典・確認範囲

  1. [1] Web Security Academy: SQLインジェクションPortSwigger / 確認日 2026-11-01