【ばいんどへんすう】

バインド変数 とは?

最終更新:
💡 SQLの「穴埋め問題方式」、安全にユーザー入力を組み込む仕組み

SQLの中に「?」や「$1」などのプレースホルダを置き、値を後から別に渡す仕組み。値がSQL文として解釈されないためSQLインジェクション対策の基本になり、DBによっては実行計画の再利用にも役立つ。

📌 このページのポイント
バインド変数 ― 安全なSQL vs 危険なSQL ✓ バインド変数(安全) SELECT * FROM users WHERE id = ? ↓ パラメータとして渡す ? → "123" SQLインジェクション防止 ✗ 文字列結合(危険) SELECT * FROM users WHERE id = ' + input + ' ↓ 悪意ある入力 input → "1' OR '1'='1" 全データ漏洩の危険!
バインド変数と文字列結合の比較
ひよこ ひよこ
バインド変数って何のためにあるの?
ペンギン先生 ペンギン先生
いちばんの目的はSQLインジェクション対策だよ。「WHERE name = ?」のようにSQLの形を先に決めて、?に入る値は別に渡す。するとDBは「ここはコード、ここはデータ」と区別できるから、入力に記号が混ざっていても値として扱われるんだ
ひよこ ひよこ
使わないとどんな問題が?
ペンギン先生 ペンギン先生
文字列をつなげてSQLを作ると、「' OR '1'='1」のような入力でWHERE条件そのものが書き換わって、見えてはいけないデータまで取り出されることがある。これがSQLインジェクションだよ。OWASPも、パラメータ化したクエリを第一の防御策に挙げているんだ
ひよこ ひよこ
Pythonでの書き方は?
ペンギン先生 ペンギン先生
「cursor.execute('SELECT * FROM users WHERE email = %s', (email,))」のように、SQLと値を別の引数で渡すよ。記号はドライバーごとに決まっていて、psycopgなら%s、sqlite3なら?を使う。%sだからといってPythonの%演算子で文字列に埋め込むと、ただの文字列連結になって意味がなくなるから注意してね
ひよこ ひよこ
バインド変数を使えば何でも安全なの?
ペンギン先生 ペンギン先生
守れるのは「値」の部分だけなんだ。テーブル名や列名、ORDER BYのASC/DESCはプレースホルダにできないから、そこにユーザー入力を入れるなら、そのまま使わずに「決められた候補から選ぶ」形にする必要があるよ
ひよこ ひよこ
速くなるって聞いたけど本当?
ペンギン先生 ペンギン先生
DBによってはそうだよ。Oracleでは、値だけ違うSQLを毎回文字列で作ると別のSQLとして扱われて「ハードパース」が繰り返されるけど、バインド変数を使えば同じSQLとして実行計画を共有できる。ただ、値によって最適な計画が違うこともあって、PostgreSQLのPREPAREは最初の5回を値ごとの計画で実行してから、共通の計画を使うかを判断しているんだ
もっと詳しく知りたい人へ

列名や並び順をユーザーに選ばせたいときはどうする?

入力をそのままSQLに入れず、アプリ側で許可した候補に対応づけるよ。たとえば並び順なら、入力を「昇順か降順か」の真偽値に変換してから、プログラムがASCかDESCのどちらかを選んでSQLに書く。OWASPのチートシートでも、バインド変数が使えない部分には入力の検証やクエリの設計変更で対処するよう勧めているよ。

ペンギン
まとめ:ざっくりこれだけ覚えればOK!
バインド変数って出てきたら「SQLの形と値を分けて渡し、値をSQLとして解釈させない仕組み」と思えばOK!
📖 おまけ:英語の意味
「Bind Variable」 = 束縛変数・変数の紐付け
💬 SQLの中の変数に値を結びつける(bind)ことから来ているよ。事前にSQLを準備して値を後から渡す「プリペアドステートメント(Prepared Statement)」と一緒に使われることが多いんだ

参考資料

← 用語集にもどる