セキュアなSQLの基本5つ|初心者が最初に覚えること

その他

SQL を書き始めると、「動けばよい」になりがちです。ところがユーザー入力をそのまま SQL に入れると、意図しないデータ取得や改ざんにつながることがあります。

この記事では、攻撃手法の暗記ではなく、初心者が最初に身につけるべきセキュアなSQLの基本5つを、理由とセットでまとめます。

この記事でわかること

  • セキュアなSQLで最初に覚える5つの基本
  • 「動くSQL」と「安全なSQL」の違い
  • PHP / WordPress での具体的な書き方の方向性
  • 安全そうに見える危ない書き方の見分け方

こんなときに読むと役立ちます

  • SQL は書けるが、セキュリティの話になるといきなり難しく感じる
  • 「SQLインジェクション」という言葉は聞いたが、何をすればよいか曖昧
  • WordPress の $wpdb を使うとき、どこまで任せていいかわからない
  • チュートリアル通りのコードを本番にそのまま使っていいか不安

まず押さえる:セキュアなSQLとは

ユーザー入力や外部データを「値」として安全に渡し、SQL の構造そのものは開発者が固定する書き方

ポイントは「入力を信用しない」ことです。フォームや URL、API の値は、すべて検証・加工の対象と考えます。

基本5つ:一覧

# 基本 一言で
1 ユーザー入力を SQL 文字列に直接入れない 連結しない
2 プリペアドステートメントを使う 値と SQL を分ける
3 DB ユーザーは必要最小限の権限だけ 権限を絞る
4 エラーメッセージをそのまま画面に出さない 情報を漏らさない
5 フレームワークの安全機能を使う 自前実装を減らす

1. ユーザー入力を SQL 文字列に直接入れない

いちばん多いのが、変数を "..." でくっつける書き方です。

// 危険:ユーザー入力 $email をそのまま SQL に連結
$sql = "SELECT * FROM users WHERE email = '" . $email . "'";

入力次第で、SQL の意味そのものが変わってしまいます。「動いたから大丈夫」はセキュアの根拠にならない、と覚えておいてください。

方針: SQL 文の形はコード側で固定し、変わるのは値だけにします。

2. プリペアドステートメントを使う

プリペアドステートメントは、SQL の枠組み渡す値を分けて送る仕組みです。値は「データ」として扱われ、命令として解釈されにくくなります。

// PDO の例
$stmt = $pdo->prepare('SELECT * FROM users WHERE email = :email');
$stmt->execute(['email' => $email]);

「エスケープしたから大丈夫」だけに頼るより、プリペアドを第一選択にするのが安全です。

WordPress なら

$wpdb->prepare() を使います。プレースホルダは %s(文字列)、%d(整数)、%f(浮動小数)です。

$sql = $wpdb->prepare(
  'SELECT * FROM {$wpdb->users} WHERE user_email = %s',
  $email
);
$users = $wpdb->get_results( $sql );

3. DB ユーザーは必要最小限の権限だけ

アプリが使う DB ユーザーに DROPGRANT など、不要な権限を付けないのもセキュアなSQLの一部です。

  • 参照だけなら SELECT のみ
  • 更新系は INSERT / UPDATE / DELETE までに絞る
  • 本番と開発でユーザーを分ける

たとえ SQL の書き方に不備があっても、被害の広がりを小さくする効果があります。

4. エラーメッセージをそのまま画面に出さない

DB のエラーには、テーブル名・カラム名・SQL の断片などが含まれることがあります。開発中はデバッグに便利ですが、本番でそのまま表示すると攻撃の手がかりになります。

  • 本番ではユーザー向けに汎用的なメッセージだけ表示する
  • 詳細はログに記録し、開発者だけが見られる場所に残す
  • WordPress では WP_DEBUG / WP_DEBUG_DISPLAY の設定に注意する

5. フレームワークの安全機能を使う

「自分でエスケープ関数を書く」より、検証済みの仕組みに任せる方が安全です。

環境 使うものの例
WordPress $wpdb->prepare()WP_Query、サニタイズ関数群
PHP 一般 PDO のプリペアド、ORM のクエリビルダ
生 SQL が必要なとき プレースホルダ付きで書き、入力は必ずバインド

ORM や高レベル API を使っていても、whereRaw() のように生 SQL を混ぜる箇所は要注意です。そこだけルールが崩れがちです。

安全そうに見える落とし穴

書き方 なぜ足りないか
intval() したから数値は安全 数値以外のパラメータや、別の SQL 部分は別問題
クォートで囲んだ エスケープ漏れ・エンコーディングの問題が残る
管理画面だけだから 権限のあるユーザー経由でもリスクはある
社内ツールだから 内部脅威・設定ミス・権限昇格は起こりうる

コードを書くときのチェックリスト

  1. SQL 文字列に . でユーザー入力を連結していないか
  2. プリペアド(または $wpdb->prepare())を使っているか
  3. テーブル名・カラム名にユーザー入力を入れていないか(値と構造を混同していないか)
  4. 本番で DB エラーがそのまま表示されないか
  5. DB ユーザーの権限が必要以上に広くないか

新しい SQL を書いたら、「この入力はどこから来て、どのプレースホルダに渡しているか?」と1行ずつ確認する習慣が、いちばん効きます。

まとめ

  • 1. ユーザー入力を SQL に直接連結しない
  • 2. プリペアドステートメントで値を渡す
  • 3. DB ユーザーは最小権限
  • 4. 本番で DB エラーを見せない
  • 5. $wpdb->prepare() など、フレームワークの安全機能を使う

次に SQL を書くときは、まず「この値はプレースホルダ経由か?」と自分に問いかけてみてください。セキュアなSQLは、難しい攻撃対策より毎回の小さな習慣の積み重ねから始まります。

タイトルとURLをコピーしました