PHPのプリペアドステートメントを完全ガイド!初心者でも分かるSQLインジェクション対策
生徒
「PHPでデータベースを使うときって、悪い人に操作される危険があるって聞いたんですが、本当ですか?」
先生
「そうですね。特にSQLインジェクションという攻撃はとても有名で、初心者のころに必ず理解しておきたいポイントです。」
生徒
「SQLインジェクションって難しそう…。どうすれば防げるんですか?」
先生
「PHPではプリペアドステートメントという仕組みを使うことで、安全にデータベースを操作できます。仕組みと具体例をこれから説明しますね。」
1. SQLインジェクションとは?初心者でも分かる危険な攻撃の仕組み
PHPでデータベースを操作する場合に必ず理解してほしいのが「SQLインジェクション」という攻撃です。これは、入力欄に悪意ある文字を入れることで、プログラムが本来想定していない命令を実行させてしまう攻撃のことです。例えば、ログインフォームに名前を入力するだけでデータベースの情報を盗まれたり、削除されたりする危険があります。
初心者がよく書くコードでは、ユーザーが送った文字をそのままSQL文にくっつけてしまうことがあります。これはとても危険で、たとえば以下のような入力があったとします。
' OR '1'='1
このような文字が送られると、データベースは「常に正しい条件」と判断してしまい、ログインチェックを突破される可能性が出てきます。これがSQLインジェクションです。
2. SQLインジェクションを防ぐにはプリペアドステートメントが最強
PHPではSQLインジェクションを防ぐための仕組みとして「プリペアドステートメント」が用意されています。これはデータベースに送るSQL文と、実際のデータ(ユーザーが入力した内容)を完全に分けて扱う技術です。
プリペアドステートメントを使うと、データベースは「これは命令ではなくただの値だな」と認識するため、どれだけ変な入力をされても勝手にSQL文として実行されなくなります。つまり、どんなに悪意のある入力がきても安全なのです。
3. PHP(PDO)でプリペアドステートメントを使う実例
PHPでデータベースを扱うとき、もっとも一般的なのがPDO(PHP Data Objects)という方法です。ここでは初心者でもできるよう、基本のログイン確認の例を使って説明します。
<?php
$pdo = new PDO('mysql:host=localhost;dbname=test;charset=utf8', 'user', 'password');
$stmt = $pdo->prepare('SELECT * FROM users WHERE name = :name');
$stmt->bindValue(':name', $_POST['name'], PDO::PARAM_STR);
$stmt->execute();
$result = $stmt->fetch();
?>
このコードでは次のように安全な処理を行っています。
- prepare() で SQL 文のひな型をあらかじめ作る
- bindValue() でユーザー入力を安全にセットする
- execute() で実行する
SQL文と値が分離されているため、どんな入力が来てもSQLインジェクションは起きません。
4. MySQLiでもプリペアドステートメントは使える
PHPにはPDO以外に「MySQLi」というデータベース操作の方法もあります。こちらでもプリペアドステートメントを利用できます。
<?php
$mysqli = new mysqli('localhost', 'user', 'password', 'test');
$stmt = $mysqli->prepare('SELECT * FROM users WHERE name = ?');
$stmt->bind_param('s', $_POST['name']);
$stmt->execute();
$result = $stmt->get_result();
?>
PDOと同じく、SQL文とユーザーの入力が完全に分離されるため、安全なデータベース処理ができます。
5. プリペアドステートメントがなぜ安全なのか?初心者向けのたとえ話で解説
プリペアドステートメントの安全性を理解するために、簡単なたとえを使ってみましょう。
例えば、料理をするときに「レシピ(SQL文)」と「材料(ユーザーが入力する値)」を分けますよね。プリペアドステートメントはこれとまったく同じで、レシピを先に確定させ、材料が変なものでも料理は変わらないようにする仕組みです。
これにより、たとえ悪意ある文字が入力されても、命令として扱われないため安全なのです。
6. 初心者がやりがちな危険な例と安全な書き方の比較
初心者がついやってしまう危険な書き方を紹介し、その後に安全な書き方を示します。
危険なコード(絶対に書いてはいけない)
<?php
$name = $_POST['name'];
$sql = "SELECT * FROM users WHERE name = '$name'";
$result = $pdo->query($sql);
?>
ユーザーの入力がそのままSQLに組み込まれているため、SQLインジェクションが起きます。
安全なコード(必ずプリペアドステートメントを使う)
<?php
$stmt = $pdo->prepare("SELECT * FROM users WHERE name = :name");
$stmt->bindValue(':name', $_POST['name'], PDO::PARAM_STR);
$stmt->execute();
?>
SQL文と値が分離されているため、安全にデータベースを扱えます。