你的 CakePHP 应用安全吗?

Posted on 10th September 2007 by Nio in CakePHP, 程序人生

最近在写 CakePHP 网站的时候,发现一个 Cake 开发人员经常犯的错误,这个错误实际上会成为网站安全漏洞,使得网站很容易被攻击。

先来看如下代码,这是一个用户注册的 action,通常的写法是:


<?php
class UsersController extends AppController {
    
    //....
    
    function register() 
    {
        if (!empty($this->data)) {
            $this->User->create();
            if ($this->User->save()) {
                $this->redirect(array('action'=>'index'), nulltrue);
            } else {
                $this->Session->setFlash(__('Failed to register. Please, try again.'));
            }
        }
    }
    
    //....
    
}
?>

看起来这个代码片断很简单,很寻常,所以也很流行。但这个流行却让很多开发人员忽略了安全性问题。实际上我们并没有对表单提交过来的数据进行过滤,也就是说你可以通过客户端,给 $this->data 加入各种字段,只要这些字段名和数据表中的能够对上,都会被保存进数据库中。最致命的字段是什么?我们都知道,Cake 的表设计通常都有一个 'id' 字段,这也是为了方便快速编程而设计的默认表关键字。这个 id 就是最致命的字段。测试的方式很简单,通常我们可以通过 url 或者其它方式知道某个用户的 id,那么在注册的时候,使用 Firebug 编辑 HTML 表单,加入一个输入 id 的文本框:


<input type="text" name="data[User][id]" />

然后你就可以在表单中填入一个已存在的用户 id,如果你不知道任何用户的 id,那么可以试一下 1,通常前几个用户都是网站的相关人员的帐号,也极有可能拥有比普通用户更多的权限。其它注册项仍然照常填写,完成之后提交。接下来会发生什么呢?register() 方法把你的信息写入数据库了,但并不是创建一个新的用户,而是覆盖了你所填写的用户 id 所对应的那个用户,实际上你已经可以修改到那个用户的密码了。

想要修复这个问题,必须对 $this->data 进行过滤,至少要 unset($this->data['User']['id']); ,最保险的是把你真正需要的数据保存在一个数组里边,然后传给 $this->User->save(),如:


<?php
//....
if (!empty($this->data)) {
    $this->User->create();
    $data = array(
        'username' => $this->data['User']['username'],
        'password' => $this->data['User']['password'],
        //....
    );
    if ($this->User->save($data)) {
        $this->redirect(array('action'=>'index'), nulltrue);
    } else {
        $this->Session->setFlash(__('Failed to register. Please, try again.'));
    }
}
//....
?>

安全问题是最为重要的,不管你用什么框架,不管它有多方便,你都要去关注这方面的问题,不要被表面的“快速开发”所麻痹。

3 Comments »

  1. model中save方法的第三个参数就是干这个的:
    /**
    * Saves model data to the database.
    * By default, validation occurs before save.
    *
    * @param array $data Data to save.
    * @param boolean $validate If set, validation will be done before the save
    * @param array $fieldList List of fields to allow to be written
    * @return boolean success
    */
    function save($data = null, $validate = true, $fieldList = array())

    Comment by 老王 — September 13, 2007 @ 10:46 am

  2. to 老王:这第三个参数对 id 起不到过滤作用的,因为 id 在 $this->set($data);(save() 方法中)的时候就被设置到 Model::$id 属性中的,而这个 id 恰恰被用于判断是 update 还是 create。你可以在表单中加入一个 hidden 的 id 字段,指向已存在的记录,测试一下就一目了然了。

    Comment by Nio — September 13, 2007 @ 3:17 pm

  3. 看了一下cake的源代码,还真是这样,save之前调用create形同虚设,看来这个cake的一个bug,不应该允许id随便设置。

    Comment by 老王 — September 13, 2007 @ 4:58 pm

Leave a comment