HOME / BLOG
BLOG

PHPでPDOのプリペアドステートメントを使う方法【SQLインジェクション対策】

デフォルト画像

PHPでデータベースを扱うとき、ユーザー入力をSQL文字列へ直接つなげるのは危険です。検索フォーム、ログイン、管理画面、APIなどでは、PDOのプリペアドステートメントを使って値をバインドするのが基本です。

この記事では、検索キーワードを受け取って記事一覧を取得する例で、PDOの安全な書き方をまとめます。

避けたい書き方

<?php
$keyword = $_GET['keyword'] ?? '';
$sql = "SELECT * FROM posts WHERE title LIKE '%{$keyword}%'";
$posts = $pdo->query($sql)->fetchAll();

PDOで安全に書く例

<?php
$pdo = new PDO(
    'mysql:host=localhost;dbname=app;charset=utf8mb4',
    'user',
    'password',
    [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        PDO::ATTR_EMULATE_PREPARES => false,
    ]
);

$keyword = $_GET['keyword'] ?? '';

$stmt = $pdo->prepare(
    'SELECT id, title, slug
     FROM posts
     WHERE title LIKE :keyword
     ORDER BY id DESC
     LIMIT 20'
);

$stmt->execute([
    ':keyword' => '%' . $keyword . '%',
]);

$posts = $stmt->fetchAll();

foreach ($posts as $post) {
    echo htmlspecialchars($post['title'], ENT_QUOTES, 'UTF-8') . PHP_EOL;
}

設定の意味

設定意味
PDO::ATTR_ERRMODEエラー時に例外を投げて原因を追いやすくする
PDO::ATTR_DEFAULT_FETCH_MODE取得結果を連想配列で扱いやすくする
PDO::ATTR_EMULATE_PREPARES可能な範囲でDB側のプリペアを使う
charset=utf8mb4絵文字や日本語を安全に扱いやすくする

注意点

  • 値はプレースホルダーへ渡し、SQL文字列へ直接連結しない
  • テーブル名やカラム名はプレースホルダーで置き換えられない
  • 出力時は htmlspecialchars() でHTMLエスケープする
  • WordPress内では基本的に $wpdb->prepare() を使う。独自PHPや小さな管理ツールではPDOが使いやすい

あわせて読みたい

参考資料