Home > アーカイブ > 2007-07

2007-07

CakePHP 比較演算子インジェクションに注意

この記事の所要時間: 239

@deprecated

この情報はCakePHP1.2betaまでのものです。1.2RC1についてはこの方法は有効ではありません。詳しくはCakePHP 1.2RC1からは比較演算子をキーに書くをどうぞ。

CakePHPのモデルで検索条件を指定する場合は比較演算子に注意が必要です。

検索条件では↓な感じで条件値の他にSQLの比較演算子を入れることができます。

<?php
class UserController extends AppController {
  funtion index($id) {
     $id = is_numeric($id) ? $id : 0;
     // $id より大きなidを持つレコードを取得
     $list = $this->findAll(array('id' => '> ' . $id)); 
     $this->set('list', $list);
  }
}
?>

これを見ただけで分かる人はピンと来ますね。そう外部から送られてきた値に比較演算子が含まれていてもそれがそのままSQL文として動作してしまうわけです。

これがマズそうな場面は色々あるでしょうけど、決定的なのがログイン画面です。例としてメールアドレスとパスワードで認証するアクションを見てみます。

<?php
    function login() {
      if (!empty($this->data['User'])) {
        $conds = array();
        $conds['email'] = $this->data['User']['email'];
        $conds['password'] = $this->data['User']['password'];
        $user = $this->User->find($conds);
        if (!empty($user)) {
          // ログイン成功
        }
      }
    }
?>

まあありがちな処理だと思いますが、このアクションに[email=!=hoge@example.com&password=!=a]をPOSTすると↓のようなSQL文が発行されてしまいます。(WHERE句以降)

WHERE "email" != 'hoge@example.com' AND "password" != 'a' LIMIT 1

これだとusers.emailにhoge@expample.com、users.passwordにaのどちらもが存在しなければログインする事が可能となります。つまりユーザ情報に登録されていなさそうな値を使えば誰でもログインが可能となります。

他にも登録されているメールアドレスを入力して、パスワードだけ[!=]を使うという手もあります。これならユーザのメールアドレスさえ分かればどのユーザとしてもログインできてしまいます。

この問題の対策は2つ+1つあります。

1. findBy/findAllByを使う

findBy/findAllByを使うとこの現象は発生しません。先程のloginアクションを書き換えてみます。

<?php
//        $user = $this->User->find($conds);
      $user = $this->User->findByEmailAndPassword($conds['email']
                                                          , $conds['password']);

これなら入力された比較演算子も文字列として処理されます。

WHERE "User"."email" = '!=hoge@example.com' AND "User"."password" = '!=a' LIMIT 1

2. 入力値の前に’= ‘を付ける

findやfindAllで条件を連想配列で渡したい場合は各値の前に’= ‘を付けます。実はfindBy/findAllByでは内部的にこの処理を行ってます。つまりそれを自分でやってしまうというわけです。

ここでのポイントは’=’の後ろにスペースを入れるという事です。これが無いと一部の演算子が有効となってしまいます。

<?php
        $conds = array();
        $conds&#91;'email'&#93; = '= ' . $this->data['User']['email'];
        $conds['password'] = '= ' . $this->data['User']['password'];
        $user = $this->User->find($conds);
?>

3. 入力値を厳密にチェックする

これももちろん大事です。検索に使用する値が数値のみや日付形式といった制限がある場合はそちらのチェックでこの問題を防ぐことができます。

ただ、実は比較演算子の他に[like/ilike/in/or/not/in/between/regexp/similar to]といったキーワードも同様の問題をはらんでいます。これらは文字列の中では通常の単語として出てくる可能性もある(「Like a child」とか)ので判別が難しいです。

ちなみにこの問題が発生するのは比較演算子・キーワードが文字列の先頭になっている場合のみです。それ以外の箇所にある場合は問題ありません。

find/findAllで検索条件に連想配列を使う場合は注意を

これは忘れるとインパクトがあるのでfind/findByfindBy/findAllByで検索する場合はくれぐれもご注意を。

@see: CakePHPの何か「CakePHPのModelを使う」

Home > アーカイブ > 2007-07

検索
フィード
メタ情報

Return to page top