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 を使うと、当然記述は煩雑になってくるけど、この程度の手間とリスクを秤にかければ、書いた方がいいと思う。
少し調べてみたが、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 を使うと、当然記述は煩雑になってくるけど、この程度の手間とリスクを秤にかければ、書いた方がいいと思う。