TL

SQL インジェクション

入力欄に書いた文字列を SQL の一部として実行させてしまう攻撃。ログイン回避やデータ全件漏洩につながる。文字列連結をやめ、プレースホルダ(プリペアドステートメント)で値を「データ」として渡すのが本質的対策。

中級セキュリティSQLWebアプリプリペアドステートメント最終更新: 2026-06-04
TL;DR要点だけ先に
  • 1.ユーザー入力を文字列連結で SQL に埋め込むと、入力が「データ」ではなく「命令」として実行される。これが SQL インジェクション。
  • 2.結果はログイン回避・テーブル全件漏洩・改ざん・削除まで。Web 攻撃の中でも被害が桁違いに大きい古典的かつ現役の脅威。
  • 3.本質的対策はただ一つ、プレースホルダ(プリペアドステートメント)で値とクエリを分離すること。エスケープや入力検証は補助でしかない。

なぜ起きるのか:データと命令が混ざる

問題の根は「文字列連結で SQL を組み立てている」一点に尽きます。次のコードは、ユーザー名で会員を1件引くつもりの典型的な脆弱コードです。

// ❌ 脆弱:入力をそのまま文字列に連結している
const name = req.query.name; // 例: ユーザーが入力した値
const sql = "SELECT * FROM users WHERE name = '" + name + "'";
db.query(sql);

namealice なら、組み上がる SQL は ... WHERE name = 'alice' で問題ありません。ところが攻撃者が name' OR '1'='1 を入れると、SQL はこう変わります。

-- 攻撃者の入力で組み上がってしまう SQL
SELECT * FROM users WHERE name = '' OR '1'='1'

'1'='1'常に真なので、WHERE の条件が無効化され、全ユーザーが返ります。プログラムは「名前という値を渡した」つもりでも、DB から見れば「条件式を書き換える命令」が届いている。入力(データ)のはずのものが、SQL の構文(命令)として効いてしまう——これが SQL インジェクションの正体です。

何が危険か:3つの攻撃パターン

「ただ1件多く返るだけ」では済みません。混ぜ込める断片次第で、被害の質が変わります。

手口やられること入力の例(イメージ)
認証回避WHERE 条件を常に真にしてログインを突破' OR '1'='1' --
UNION 攻撃別テーブルの中身を結果に連結して抜き出す' UNION SELECT card_no, cvv FROM cards --
スタッククエリ / 破壊; で2文目を追加し更新・削除を実行'; DROP TABLE users; --
ブラインド真偽や応答時間の差から1ビットずつ推測' AND (SELECT ...) --

特にログイン回避は、認証の議論で前提にしている「パスワード照合」そのものを飛び越えます。次は典型的な脆弱ログインです。

// ❌ 脆弱:ログイン判定の SQL に入力を連結
const sql =
  "SELECT * FROM users WHERE id = '" + id + "' AND pw = '" + pw + "'";
// 攻撃者が id 欄に  admin' --  を入力すると…

idadmin' -- を入れると、-- 以降はコメント扱いになり、AND pw = ... の条件が丸ごと消えます。

SELECT * FROM users WHERE id = 'admin' --' AND pw = '(なんでも)'

パスワードを一切知らなくても admin でログインできてしまう。Cookie やセッションで認証状態をどれだけ厳密に管理しても、入口の SQL が破れていれば意味がありません

ありがちな“なんちゃって対策”はほぼ効かない

「シングルクォート ' を消す」「危険そうなキーワードを正規表現でブロックする」といった自前フィルタは、回避手段が無数にあり破られます。クォートが不要な数値カラム(WHERE id = ' + id)では 1 OR 1=1 だけで成立し、キーワードは大小文字・コメント挿入・URL/16進エンコードなどで容易にすり抜けます。ブラックリスト方式は SQL インジェクション対策として信頼してはいけません

本質的対策:プレースホルダで「値」として渡す

唯一にして最強の対策は、SQL の構文と、後から差し込む値を、最初から分離すること。これを実現するのが**プレースホルダ(プリペアドステートメント)**です。SQL 文には?$1 といった「穴」だけを書いておき、値は別経路で渡します。

// ✅ 安全:プレースホルダで値を「データ」として渡す
const sql = "SELECT * FROM users WHERE id = ? AND pw = ?";
db.query(sql, [id, pw]); // 値は SQL とは別に渡される

ポイントは、DB は ? の中身を「構文」として解釈しないこと。idadmin' -- が来ても、それは「admin' -- という名前の文字列」として WHERE 条件の値に収まるだけで、コメントや OR として効くことはありません。文字列連結による「値が命令に化ける」経路そのものが断たれます。

ORM や“バインド変数”でも、抜け道を作れば台無し

プレースホルダは ORM やクエリビルダを使えば多くの場合自動で効きます。ただし生 SQL に文字列を埋め込む口(多くの ORM が持つ raw / whereRaw / テンプレートリテラルでの組み立て)を使った瞬間、同じ穴が開きます。「ORM だから安全」ではなく、「値は必ずバインド経由」「SQL 文に外部入力を連結しない」が守るべき原則です。

対策立ち位置効果と注意
プレースホルダ / プリペアドステートメント本命(これが対策の核)値と構文を分離。ほぼ全ての注入を原理的に防ぐ
最小権限(least privilege)被害の封じ込め万一漏れても、アプリ用ユーザーに DROP / 他テーブル権限を与えない
入力検証(型・長さ・許可リスト)補助・多層防御数値は数値・列名は許可リスト化。単体では不十分
エスケープ最後の手段プレースホルダが使えない箇所のみ。自前実装は避ける
エラーメッセージを返さない情報源を絞るDB エラー全文の露出はブラインド攻撃の手掛かりになる

プレースホルダで値は守れますが、テーブル名や列名、ORDER BY の対象はプレースホルダで渡せません(構文の一部だから)。これらを動的に変えたい場合は、あらかじめ用意した許可リスト(ホワイトリスト)に照合し、一致したものだけを使います。入力をそのまま列名に流してはいけません。

// ✅ 列名のような“構文部分”は許可リストで固定する
const allowed = { name: 'name', date: 'created_at' };
const col = allowed[req.query.sort] ?? 'created_at'; // 一致しなければ既定値
const sql = `SELECT * FROM users ORDER BY ${col}`;    // col は安全な定数のみ

多層防御:万一に備える最小権限

プレースホルダを徹底しても、コードのどこか1箇所で漏れる可能性はゼロにできません。そこで「破られても被害を最小化する」二段目として、最小権限が効きます。アプリが接続する DB ユーザーに、業務に必要な権限だけを与えるのです。

アプリ用 DB ユーザーは“フル権限”にしない

読み取り中心の画面用ユーザーに INSERT/UPDATE/DELETE を与えない、まして DROP TABLE や他スキーマへのアクセスを許さない——こうしておけば、仮に注入されても '; DROP TABLE users; -- は権限エラーで弾かれます。アプリは管理者権限(root / superuser)で DB に繋がない、が鉄則。プレースホルダ(注入を防ぐ)と最小権限(被害を抑える)は、役割が違うので両方やるものです。

まとめ:覚えておくべき一行

SQL インジェクションは「入力を文字列連結で SQL に混ぜる」から起きる、古典的かつ今も多発する攻撃です。フィルタやエスケープで小手先に防ごうとすると必ず穴が残ります。

結論

外部入力は、SQL 文に連結せず、必ずプレースホルダ(プリペアドステートメント)で「値」として渡す。 これが本質的対策。加えて、列名など構文部分は許可リスト、DB ユーザーは最小権限——この3点で守ります。同じ「入力をそのまま信用した」系の攻撃として、ブラウザ側で起きる XSS も合わせて押さえておくと、Web の入力検証の勘所がつかめます。

セキュリティ Article

SQL インジェクションを実務で読む

TL;DRは入口です。実際に選ぶ・使う段階では、何を解決するか、何と比較するか、導入後にどこで詰まるかまで見る必要があります。

解決すること

セキュリティ

比較で見る軸

難易度: intermediate / カテゴリ: セキュリティ / タグ数: 4

導入後に効く点

結果はログイン回避・テーブル全件漏洩・改ざん・削除まで。Web 攻撃の中でも被害が桁違いに大きい古典的かつ現役の脅威。

先に潰すリスク

用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。

数字・仕様の読み方
難易度
intermediate
カテゴリ
セキュリティ
タグ数
4

判断チェックリスト

  • 自社の用途が「セキュリティ / SQL」に近いか確認する。
  • 強みである「ユーザー入力を文字列連結で SQL に埋め込むと、入力が「データ」ではなく「命令」として実行される。これが SQL インジェクション。」が本当に評価軸になるか確認する。
  • 注意点の「用語だけ覚えても、設計・実装・運用でどこに効くかを確認しないと判断を誤る。」を運用で吸収できるか確認する。
  • 公開値や仕様値は、対象プラン・対象機種・対象リージョンまで確認する。
  • 既存システム、ID、ネットワーク、監視、バックアップとの接続方法を先に洗い出す。
  • 小さく試してから、本番移行、権限設計、障害時手順、コスト監視を決める。

次に確認する観点

セキュリティSQLWebアプリプリペアドステートメントセキュリティSQLWebアプリプリペアドステートメント
参考: 公式情報