3秒でわかる
SQL 文の値の位置だけを記号で空けておき、実行時に値を別途渡す書き方。SQL インジェクションを防ぐ基本の手段になります。
もう少し詳しく
どういうものか
プレースホルダは、SQL 文の中で値が入る位置だけを ? や $1 のような記号で空けておき、実際の値は実行時に別の引数として渡す書き方になる。DB 側は先に SQL の構造を解析し、あとから届いた値を「値」としてのみ扱う。構造と値を別々の経路で渡す、という点が本質になる。バインド変数や準備済み文と呼ばれることもある。
なぜ必要か
文字列連結で SQL を組み立てると、利用者の入力がそのまま命令として解釈されうる。検索欄に ' OR '1'='1 と入力されれば条件が常に真になり、全件が漏れる。プレースホルダを使えば、同じ入力は「そういう文字列を探す」条件として扱われ、命令にはならない。あわせて、同じ SQL を繰り返し実行するときに解析結果を再利用できるため、速度の面でも有利になる。
具体例
# 危険な書き方
cur.execute("SELECT * FROM users WHERE name = '" + name + "'")
# プレースホルダを使う
cur.execute("SELECT * FROM users WHERE name = ?", (name,))Node.js の mysql2 でも考え方は同じになる。
const [rows] = await conn.execute(
"SELECT * FROM users WHERE age > ? AND city = ?",
[20, "東京"]
);つまずきやすいところ
記号は DB や言語で異なる。SQLite と MySQL は ?、PostgreSQL は $1 $2、Oracle は :name の形を取る。移植のときはここで詰まる。
引用符を自分で付けるのも間違いになる。WHERE name = '?' と書くと、疑問符という文字を探す条件になってしまう。囲みは不要で = ? と書く。
テーブル名や列名には使えない、という制限も押さえておく。プレースホルダが差し込めるのは値の位置だけで、SELECT * FROM ? は通らない。並べ替える列を利用者に選ばせたい場合は、許可する列名の一覧を自分のコードに持ち、その中から選ぶ形にする。
覚え方
SQL の骨組みは先に固めて、肉付けの値だけ後から渡す。骨組みが固まったあとの値は、何が来ても骨組みを変えられない。
似た用語との違い
エスケープ処理は、危険な記号を無害な形へ書き換えてから文字列として埋め込む方法になる。処理の漏れが 1 か所でもあれば穴が残るため、値の受け渡しはプレースホルダに任せる。なお、HTML の入力欄に薄く表示される案内文も placeholder 属性と呼ぶが、こちらは全く別の話になる。