PHP の DB アクセスは PDO に決まりだと思う理由
はじめに
PHP から DB アクセスする方法は多岐に渡り、その中でもメジャーなのは PDO (PHP Data Object) と Pear の MDB2 なのではないかと思います。
他にも組み込み関数の、Mysql 関数や Mysqli 関数があるようですが、データベースを選ばないということでは、PDO と MDB2 に軍配があがるのではないでしょうか。
PDO と MDB2 のどちらを選ぶ?
では、PDO と MDB2 のどちらを選ぶべきか悩むと思います。私もかなり悩み結構調べていました。同じように悩んでいる方は多いのではないでしょうか。
このサイトでは、MDB2 の使用を推奨してきたのですが、結論から言うと現時点では PDO に軍配があがると思います。
その理由は、MDB2 はこちらのサイトを見ると分かりますが、stable のリリースが 2007年5月3日で終わっていて実質メンテナンスが終了していると思われることです。beta のリリースも 2012年10月29日で終わっています。
MDB2 が安定していてメンテナンスの必要がないという理由なら問題ないのですが、さすがに beta で2年半もリリースがないとメンテナンスが行われていないのではないかという危惧があります。
その点、PDO は PHP 標準のクラスで、メンテナンスの心配がないので安心して利用できるというメリットがあります。
これだけでも、PDO を選択する理由になるのですが、PDO は MDB2 と比較してパフォーマンスがよいという面も見逃せないと思います。MDB2 は PHP で書かれているので、PDO と比べるとパフォーマンスが若干劣ります。
PDO の落とし穴
今まで PDO のよい面を見てきたのですが、PDO にも問題がない訳ではありません。
SQL インジェクションの対策では、データベースへの接続時に DSN (Data Source Name) に文字キャラクタセットを指定する必要があるのですが、PDO は PHP 5.3.6 まで(Windowsは 5.3.7)まで文字キャラクタセットの指定ができませんでした。
PHP 5.3系は 2014年8月14日の 5.3.29 のリリースでサポートが終了しています。そう考えると PHP 5.3.5 以前の PHP は考慮しなくてよいのかと思えますが、物事には例外があります。
それは、RedHat Enterprise Linux/CentOS 6.x が PHP 5.3.3 をサポートしていて、製品のライフサポート終了時(2020年11月)まで問題なく利用可能であるということです。
このユーザーは、PDO を何も考えないで使用してしまうと、SQLインジェクションの脆弱性を作りこんでしまう可能性があります。DSN に文字キャラクタセットをセットしていても無視されてしまいます。
幸い、Linux で MySQL のユーザーであれば、PHP 5.3.3 のユーザーでも DB の接続に文字キャラクタセットを指定することができます。
PHP 5.3.6より前のバージョンの PDO MySQL で charset を指定する - Qiita
逆に言うと、対策を行っていない場合は SQLインジェクションの脆弱性がある可能性があります。PHP 5.3.3 で PDO を使用しているユーザーは対策が行われているか確認することを強く推奨します。
PDO での SQLインジェクション対策
PDO での SQLインジェクション対策は以下のようになります。
- データベースの接続時に DSN に文字エンコーディングを指定する
- 静的プレースホルダを使用する
- バインド時はデータの型を指定する
これらに対応した PDO のサンプルコードは以下のようになります。CentOS7 + PHP 5.4.16 + MySQL 5.6.23 で動作確認しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
<?php define("USERNAME", "user01"); define("PASSWORD", "pass"); try{ //文字エンコーディングを必ず指定する $dbh = new PDO("mysql:host=192.168.11.8;dbname=Test;charset=utf8", USERNAME, PASSWORD); // 静的プレースホルダを指定 $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); $stmt = $dbh->prepare("select * from Products WHERE ID = ? AND NAME = ?;"); $stmt->setFetchMode(PDO::FETCH_ASSOC); $a = 1; $b = "りんご"; $stmt->bindParam(1, $a, PDO::PARAM_INT); $stmt->bindParam(2, $b, PDO::PARAM_STR); $stmt->execute(); while ($row = $stmt->fetch()) { echo $row["ID"]; echo $row["NAME"]; echo $row["PRICE"]; } $stmt = null; } catch(PDOException $e){ echo $e->getMessage(); } |
データ型については、こちらのサイトをご確認ください。
補足
動的プレースホルダと静的プレースホルダ
説明もなしに静的プレースホルダという用語を使いましたが、プレースホルダには動的プレースホルダと静的プレースホルダの2種類があります。
動的プレースホルダは、データベースにアクセスするライブラリでプレースホルダを実現するもので、ライブラリに脆弱性があると SQLインジェクションの脆弱性が発生する可能性があります。
一方、静的プレースホルダは、データベースの機能を使用してプレースホルダを実現するので、仕組み上、SQLインジェクションの脆弱性が発生しません。
どちらを選ぶべきかは明確ですね。
詳しくは、IPA が公開している「安全な SQL の呼び出し方」を参照してください。
おわりに
駆け足で PDO を採用すべき理由を述べてきましたが、いかがだったでしょうか。
MDB2 でも悪いわけではないのですが、今後のことを考えると PDO を採用した方がよいでしょう。新規開発ならなおさらです。
但し、システムが置かれている環境により状況は変わってくることは付記しておきます。例えば、MDB2 を使用しているシステムを無理に PDO に変える必要はないということです。それは、今後のシステム全体のバージョンアップ時に対応すればよいでしょう。
この記事がお役に立てば幸いです。
参考サイト
- PDOにおける一応の安全宣言と残る問題点 | 徳丸浩の日記
- PHPでデータベースに接続するときのまとめ - Qiita
- PHP PDO bindParam()と日付型(DATE, DATETIME)とBOOL型にハマる - かもメモ
スポンサーリンク
カテゴリー:ブログ
Twitter でも、いろんな情報を発信しています。@fnyaさんをフォロー