くまきち

山と旅と家族が大事。
でも激しい物欲が理性と財布のタガを飛ばす
最近は自転車も乗ってる

CakePHP: SQLインジェクション対策(Prepared Statement利用)

2009-05-07 14:22:54 | SEまわり
SQL インジェクション対策として、Prepared Statement を使うと良いという話はよく聞くが、CakePHP の AppModel には Prepare メソッドは見当たらない。

少し調べてみたが、PHP4 でも動作するようにするというポイントと関係があるようだ。

それは置いといて、無理矢理使えないかと思って、やってみた。

別に難しいことではなく、Model::query() メソッドに PREPARE ステートメントを投げてあげるだけ。


ついでに、SQL インジェクションのテストもしてみた。

まず、対象テーブル userinfo を用意

id     serial,
username  varchar(20) not null,
passwd   varchar(64) not null


で、次のケース(正常な利用)を考える。

 1. 利用者 ID をフォームから受け取って、$id に入れておく
 2. $id に一致する username がテーブルにあるかどうかを確認

素直に考えると、操作 2 のための SQL は、

 select * from userinfo where username = '$id';

となり、この結果を見れば、該当利用者がいるかどうかはチェックできる(論理的に正しいのは select count(*) を使うやり方だけど、count() はコストが高く、結果を捨てることになってもパフォーマンスの高い select * を使う)。



次に、SQL インジェクションのテスト。

$id を次のようにする。

 $id = "''; select username,passwd from userinfo";

これで、流してみると、ユーザ ID をパスワードの一覧が取れる。



次に PREPARE / EXECUTE を使ったテスト。

 $this->Userinfo->query("prepare plan01(varchar) as select * from userinfo where name=$1");
 $res = $this->Userinfo->query("execure plan01('$id'));

こうすると、上の SQL インジェクション用の $id を使った場合、得られる結果は空になる。

なお、複数のパラメータを与える場合は、次のように引数に並べるだけ。

 $this->Userinfo->query("prepare plan01(varchar, varchar) as select * from userinfo where name=$1 and passwd=$2");
 $res = $this->Userinfo->query("execure plan01('$id', '$passwd'));


PREPARE/EXECUTE を使うと、当然記述は煩雑になってくるけど、この程度の手間とリスクを秤にかければ、書いた方がいいと思う。


最新の画像もっと見る