flawopen.com/PHPのSQL Injection/mysqliとPDO
MySQLiとPDO:安全なPrepared Statement
Prepared StatementはSQL命令を固定し、入力値を別に送信します。ラベル付きの空欄を埋めるイメージです。PDOもMySQLiも対応します。安全性はライブラリ名ではなく、値を正しくバインドすることから生まれます。
同じクエリを各APIで安全に準備する
$userId = $_GET['id']; $sql = "SELECT id, email FROM users WHERE id = " . $userId; // Both calls execute the already-built SQL string: $rows = $mysqli->query($sql); $rows = $pdo->query($sql);
// PDO $stmt = $pdo->prepare( "SELECT id, email FROM users WHERE id = :id" ); $stmt->execute(["id" => $userId]); // MySQLi $stmt = $mysqli->prepare( "SELECT id, email FROM users WHERE id = ?" ); $stmt->bind_param("i", $userId); $stmt->execute();
PDOの主な違いはデータベース間の移植性
PDOは複数ドライバー共通のAPIを提供し、MySQLiはMySQLとMariaDB向けです。PDOだけでSQLが移植可能になるわけではなく、方言・ドライバー動作・機能は異なります。
PDOはドライバーにより:idのような名前付き、または?位置指定を使えます。同じ文で混在させないでください。MySQLiは?を使います。どちらも値を束縛しますが、表・列名、キーワード、ソート方向は束縛できません。
PDOドライバーはネイティブまたはエミュレート準備を使います。MySQLではPDO::ATTR_EMULATE_PREPARESで利用可能な場合のエミュレーションを制御します。ネイティブ準備非対応や失敗時にエミュレートへ切り替わる場合があります。ドライバーの文書に従ってください。この設定は補間SQLを安全にしません。SQLを固定し値をバインドします。
どちらのライブラリでも守るSQL Injection対策
prepare()を使い、信頼できない値をすべて束縛します。SQL構造は固定し、動的識別子はサーバー側の厳格なマップか有限のクエリ群から選びます。Prepared Statementは行へのアクセス権を与えません。必要に応じて認証ユーザーの範囲に絞ります。LIKEではバインドがSQL構文注入を防ぎますが、リテラル検索用にエスケープしない限り%と_はワイルドカードです。
PDOとMySQLiの選び方
- ✓複数ドライバー共通のAPIが必要ならPDO。移行時はSQL方言とドライバー動作を確認します。
- ✓MySQL/MariaDB専用で固有APIが必要ならMySQLiを選びます。
- ✓どちらでも補間・連結・動的識別子を調べ、値を束縛し、レコード認可は別途行います。
よくある質問
いいえ。クエリを準備し値を束縛すれば、どちらも安全に値を扱えます。名前付きは読みやすさの利点ですが、識別子を束縛せず認可要件も変えません。
一律には言えません。ドライバーの対応とフォールバックは異なります。文書と要件を確認し、どの方式でもSQLを固定して値をパラメーターで渡します。
できません。パラメーターは値であり、SQL識別子やキーワードではありません。クライアントの選択肢をサーバー側の有限な信頼済みSQL断片に対応付けます。