第27回: SQLインジェクション対策 — 文字列連結でSQLを作っていませんか?

章: 第6章: セキュリティと本番運用

ユーザーの入力値をそのままSQLに埋め込んでいませんか?

"SELECT * FROM users WHERE email = '" . $email . "'" のようにPHPで文字列連結してSQLを組み立てると、' OR '1'='1 といった入力を受けたとき全データが返る脆弱性になります。これがSQLインジェクション攻撃で、OWASP Top 10に毎回ランクインする重大リスクです。

根本的な対策はただ一つ:プリペアドステートメント(プレースホルダ)を使い、SQL文と値を分離して渡すことです。

危険な書き方と安全な書き方の比較

方法 リスク 推奨度
文字列連結でSQL生成 SQLインジェクション攻撃に脆弱 使用禁止
addslashes などでエスケープ 抜け穴が存在し不完全 非推奨
プリペアドステートメント(PDO) SQL文と値が完全分離され安全 推奨
ORMのクエリビルダ経由 内部でプレースホルダを使用 推奨
生のクエリをORM経由でrawSQL 文字列連結と同様のリスクあり 要注意

チェックポイント: ORMを使っていても whereRaw()DB::statement() に変数を文字列連結で渡すとSQLインジェクションが発生します。rawメソッドには必ずバインディング引数を使ってください。

実際のコードサンプル


-- NG: 文字列連結でSQL生成
-- SELECT * FROM users WHERE email = '" + email + "';

-- OK: プレースホルダ
PREPARE stmt FROM 'SELECT * FROM users WHERE email = ?';
SET @email = 'yuki@example.com';
EXECUTE stmt USING @email;
DEALLOCATE PREPARE stmt;

まとめ & 次のステップ

  • SQL文とユーザー入力値は必ず分離し、プレースホルダ経由でバインドする
  • PHPでは PDO のプリペアドステートメントを使い、文字列連結によるSQL生成を完全に禁止する
  • ORMを使う場合も whereRaw() などの生SQLには必ずバインディング引数を渡す
  • コードレビューで「変数をSQLに直接埋め込んでいる箇所がないか」を確認するルールを設ける
  • WAF(Webアプリケーションファイアウォール)は補完策であり、プレースホルダの代替にはならない

次回は スロークエリログの活用 を学びます。勘に頼らず計測ベースでクエリを改善する方法を解説します。

Related Articles