PHPのSQLインジェクション対策を完全解説!初心者でも安全にデータベースを扱う方法
生徒
「PHPでデータベースに保存する処理を作っているんですが、SQLインジェクションって危ないと聞きました。どうやって防げばいいんですか?」
先生
「SQLインジェクションは、フォームなどに入力された文字を悪用して不正なSQLを実行されてしまう攻撃のことです。PHPでは、PDOやmysqliを正しく使うことで簡単に対策できますよ。」
生徒
「PDOって聞いたことはありますけど、どう書けば安全になるんですか?」
先生
「では、まずSQLインジェクションがどんな攻撃かを理解して、次にPDOとmysqliを使った安全な書き方を見ていきましょう。」
1. SQLインジェクションとは?初心者でもわかる危険性の解説
SQLインジェクションとは、ユーザーが入力する文字の中に「特別な命令」を混ぜて、データベースに不正な操作をさせてしまう攻撃です。例えば、ログイン画面で本来はIDとパスワードだけを入力させる部分に、悪意のあるSQL文を紛れ込ませることで、データの書き換えや削除が行われてしまうことがあります。
たとえるなら、注文書を書く欄に「配送先:倉庫→そのまま全部捨てろ」と追加で書かれてしまって、そのまま実行してしまうようなものです。普通はそんな指示は無視すべきですが、対策をしていないとPHPはそのまま命令を実行してしまいます。
2. なぜSQLインジェクションが起きるのか?
原因は「文字列をそのままSQL文にくっつけてしまうこと」です。PHPでありがちな失敗例として、下記のようなコードがあります。
$user = $_POST['user'];
$sql = "SELECT * FROM users WHERE name = '$user'";
$stmt = $pdo->query($sql);
このコードは一見正しく見えますが、ユーザーが次のように入力したらどうでしょう?
' OR '1'='1
SQL文は下記のようになり、全ユーザーの情報が表示されてしまいます。
SELECT * FROM users WHERE name = '' OR '1'='1'
これがSQLインジェクションの典型的な例です。このような危険を防ぐためには、入力された値を「そのままSQLにつなげない」ことが重要です。
3. PDOを使った安全なSQLインジェクション対策
もっとも一般的で安全な方法は「プリペアドステートメント」を使うことです。プリペアドステートメントとは、SQL文の中に「?」などのプレースホルダー(後で値を入れる場所)を用意して、入力値を自動で安全に処理してくれる仕組みのことです。
PDOを使った安全なコードは下記のようになります。
$pdo = new PDO('mysql:host=localhost;dbname=test', 'root', '');
$user = $_POST['user'];
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->bindParam(':name', $user, PDO::PARAM_STR);
$stmt->execute();
$result = $stmt->fetchAll();
ポイントは、値を prepare() と bindParam() に渡すことで、mysql側が自動で危険な文字をエスケープ(安全に変換)してくれる点です。これにより、不正なSQL文が実行されることを防げます。
4. mysqliでのSQLインジェクション対策
PHPではPDOだけでなく、mysqli を使ったデータベース接続でもSQLインジェクション対策ができます。難しそうに見えますが、基本はPDOと同じ「プリペアドステートメント」を使うだけです。
$mysqli = new mysqli("localhost", "root", "", "test");
$user = $_POST['user'];
$stmt = $mysqli->prepare("SELECT * FROM users WHERE name = ?");
$stmt->bind_param("s", $user);
$stmt->execute();
$result = $stmt->get_result();
「s」は文字列(string)の意味で、値の型を指定します。mysqliでも、prepare → bind → execute の流れで安全にSQLを実行できます。
5. SQLインジェクション対策として絶対にやってはいけないこと
初心者の方が特にやってしまいがちなNG例も紹介します。これらは危険なので避けましょう。
- ユーザー入力を直接SQLに連結する
- addslashes() でエスケープすれば大丈夫だと思い込む
- Magic Quotes(古いPHPの機能)に頼る
現代のPHPでは、プリペアドステートメントを使うことがもっとも安全で、最も推奨される方法です。
6. フォーム入力をバリデーションして安全性をさらに高める
SQLインジェクション対策には、プリペアドステートメントが絶対ですが、さらに安全性を高めるためには「バリデーション」が重要です。バリデーションとは、入力値に対して「正しい形式かどうか」を確認する処理です。
例えば、ログインフォームで「ユーザー名は20文字以内」「ひらがなは禁止」などのルールを作り、それを守らない入力をはじくことで、攻撃の入り口を狭めることができます。