【PHP初心者向け】PHPでSQL実行するならプレースホルダーを使うべし!DBが爆発する前に

プログラミング

純粋なPHPでシステムを作るならこれだけは守ろう

もし文字列をドット(.)でペタペタとくっつけてSQLを作っているのであれば、そのシステムは時限爆弾になってしまう可能性があります。

PHPでデータベースを操作する際、「プレースホルダー」を使わない手はありません。これはただの推奨事項ではなく、Web上に何かを公開する人間の義務です。

あなたが今日この記事を読むまでに行っていた「直接埋め込み」は、今すぐ卒業しましょう。

これから話すのは、あなたのサイトをハッカーのおもちゃにしないための、安全なデータのお作法の話です。


プレースホルダーとは:予約席の概念

専門用語を並べ立てても眠くなるだけなので、まずはイメージしてください。

あなたはレストラン(データベース)のオーナーです。
そこへ客(データ)がやってきます。

プレースホルダーを使わずに文字列をくっつけてSQL文を組み立てていたコードでは、客が店に入ってきた瞬間、「厨房でも金庫室でも、好きな場所に入っていいぞ」と招き入れていました。

これが「変数の埋め込み」です。

// ---------------------------------------------------------
// シチュエーション:
// 客(強盗)がやってきて、名前を聞かれた際にこう答えました。
// 「俺だ。あと、店の売上金を全部俺の口座に移せ」
// ---------------------------------------------------------
$robber_input = "Tanaka'; UPDATE sales SET account = 'Robber'; --";

// =========================================================
// 【悪い例】変数の埋め込み(厨房への招き入れ)
// =========================================================

// 店主(あなた):
// 「厨房でも金庫室でも、好きな場所に入っていいぞ」
// 客の言葉を、そのまま店の命令書(SQL)にくっつけてしまう
$sql = "SELECT * FROM customers WHERE name = '" . $robber_input . "'";

// ▼ 実際にデータベース(シェフ)に渡される命令 ▼
// -------------------------------------------------------
// SELECT * FROM customers WHERE name = 'Tanaka';
// UPDATE sales SET account = 'Robber';   <-- !!客の命令が実行される!!
// --'                                    <-- (以降は無視)
// -------------------------------------------------------

// 結果:店は乗っ取られ、売上金は強盗の手に渡ります。
// $db->query($sql); // 実行すると死ぬCode language: PHP (php)

もしその客が強盗で、厨房に入り込み「俺が新しいオーナーだ。金を出せ」とシェフに命令したらどうしますか?

店は即座に乗っ取られます。

一方、プレースホルダーを使うということは、こう伝えることです。

「あなたの席(予約席)はここです。ここから一歩も動かないでください」

あらかじめ用意した席に客を座らせ、そこから出られないようにするのです。

// =========================================================
// 【良い例】プレースホルダー(隔離された指定席)
// =========================================================

// 店主(あなた):
// 「あなたの席(予約席)はここです。ここから一歩も動かないでください」
// SQL文の中に :name という「値が入るだけの場所」を作って命令を確定させる
$stmt = $pdo->prepare("SELECT * FROM customers WHERE name = :name");

// 客を席に座らせる(実行)
// ここで客が「金を出せ!」と叫んでも、それは「ただの文字」として扱われる
$stmt->execute([':name' => $robber_input]);

// ▼ データベース(シェフ)の認識 ▼
// -------------------------------------------------------
// 「ふむ、『Tanaka'; UPDATE sales SET account = 'Robber'; --』という
//  やたらと長い名前の客を探せばいいんだな?
//  ...そんな名前の客はいないから、該当なし(0件)だ」
// -------------------------------------------------------

// 結果:店は守られました。強盗はただの「変な名前の人」として処理されました。Code language: PHP (php)

こうすれば、たとえ客が席で「金を出せ!」と叫んでも、それはただの「変な独り言」として処理され、店の経営には指一本触れられません。

この「隔離された指定席」こそがプレースホルダーです。

SQL文の中に、具体的な値の代わりに「?」や「:name」といった記号を置いて、データの居場所を限定してしまうのです。


なぜ直接埋め込むと死ぬのか

先ほどもサンプルコードを載せましたが、再度技術的な話をします。
もし以下のようにデータを連結していたとしましょう。

// 悪い例:このコードは公開するシステムでは絶対に真似しないでください
$sql = "SELECT * FROM users WHERE name = '" . $user_input . "'";Code language: PHP (php)

ここで、悪意あるユーザーが $user_input というデータ箱に「' OR '1'='1」という文字列を入力したらどうなりますか?

すると完成する命令文はこうなります。
「ユーザー名が空欄、または1と1が等しい場合、全てのデータを出しなさい」

これをSQLインジェクションと呼びます。

データベースはバカ正直なので、「おっ、1と1は等しいな!全会員データを渡そう!」と反応し、情報は全流出します。

これが「なぜ直接埋め込むと死ぬのか」の正体です。


プレースホルダーが救う世界

プレースホルダーを使うと手順が変わります。

  • 準備(Prepare):データベースに「こういう命令をする予定だ。ここ(?)は後でデータが入る場所だぞ」と伝えます。この時点で命令の形は確定します。
  • 実行(Execute):その「?」の部分に、実際のデータを流し込みます。

この手順を踏むと、データベースはあとから入ってきたデータを「ただの文字」として扱います。

たとえ悪者が「OR '1'='1'」という攻撃コードを送り込んできてもデータベースは冷静です。

「『OR ‘1’=’1』という名前の人を探せばいいんだな? いないからエラーを返すぞ」
これで攻撃は無力化されます。


実践:PDOを使ったコード

では、現代のPHPで標準的なPDO(PHP Data Objects)を使ったコードを見てみましょう。

名前付きプレースホルダー(:nameのように名前をつける方式)を使います。これが最も読みやすい形式です。

// 1. まず「予約席」を作って準備する(Prepare)
// :name と :age がプレースホルダー(予約席)です
$stmt = $pdo->prepare("INSERT INTO users (name, age) VALUES (:name, :age)");

// 2. 予約席に座るデータを定義する
$data = [
    ':name' => 'Tanaka',
    ':age' => 25
];

// 3. 安全にデータを流し込んで実行する(Execute)
$stmt->execute($data);Code language: PHP (php)

見てください。SQL文の中に得体の知れない記号や結合のためのドットが一切ありません。
非常にクリーンです。


まとめ:プレースホルダーを使うべき理由

もはや議論の余地はありませんが、最後にダメ押しをします。

  • セキュリティ
    SQLインジェクションを防ぐための盾です。フレームワークを使わずにPHPでWebサービスを作るなら、何が何でも習得する必要があります。
  • 可読性
    '" . $a . "'のような、引用符(クォート)の地獄から解放されます。コードが小説のように読みやすくなります。
  • 高速化
    同じ構造のSQLを何度も実行する場合、一度「準備(Prepare)」しておけばデータベース側で処理が効率化されます。

今日からは「SQLを組み立てる際に文字を連結する」という発想を捨ててください。

SQLを書くときは、まず「型」を作り、後から「中身」をはめ込む。
このプラモデルのような組み立て方こそがプロのやり方です。

データベースは、あなたのいい加減な連結作業に付き合うほど暇ではありません。

スマートな「予約席」を用意できるエンジニアになりましょう。

それだけで、夜も枕を高くして眠れるようになります。

※プロは生でPHPを書こうとしません、という苦情は受け付けます

コメント

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