用户
 找回密码
 入住 CI 中国社区
搜索
楼主: visvoy
收起左侧

[讨论/交流] 脆弱的CI缓存系统,1天攻陷你的CI网站

    [复制链接]
发表于 2009-4-6 18:52:24 | 显示全部楼层
补充一下,我说的controller和method都不是CI框架中的原始controller和method,而是自己写的控制器和方法,也就是自己手工认证
发表于 2009-4-6 23:48:48 | 显示全部楼层
我理解 cuimuxi 朋友的意思,在控制器方法中验证,如果不符合要求就不要执行
$this->output->cache(n);

这样就不会在这次非法访问中生成缓存。
 楼主| 发表于 2009-4-7 06:53:01 | 显示全部楼层
楼主为什么总是误会我的意思呢,难道我的表达能力就这么差

我没说让CI去干认证参数这档子事,难道就不能在controller的method中加一条小小的数据有效性认证么?

我也没说404能管得了缓存,生成缓存的文件就让它缓 ...
cuimuxi 发表于 2009-4-6 18:50

其实看明白了,你还是停留在说的阶段,

“在controller的method中加一条小小的数据有效性认证”

说起来是很容易,你有没有想过一个project每个method的segment验证是千变万化的?

“一条小小的验证”在一个project里面会变成“几十几百条小小的验证”

就拿我正在写的交友系统,粗略算一下带缓存的method少说也近百了,

如果给每个method增加小小的验证,那程序将耦合到囧rz的地步,

假如下个CI版本变更了segment相关代码,怎么办?

挨个修改你说的“几十几百个小小的验证”吗?

CI没有考虑到segment验证是她的缺失,但既然提供了cache功能,
其附带了一个极有可能被攻击的缺失,我认为这就是漏洞,
毫不知情的开发者一旦用了CI的cache,那就等于任人鱼肉,
随便几个人一起攻击,1两天服务器就挂了,这还不叫漏洞?

你可以说你为每个method加验证,可是你终究会有疏忽的时候,而那时候你就要为CI的缺失买单了
 楼主| 发表于 2009-4-7 07:11:44 | 显示全部楼层
补充一下,我说的controller和method都不是CI框架中的原始controller和method,而是自己写的控制器和方法,也就是自己手工认证
cuimuxi 发表于 2009-4-6 18:52

搞了半天白搞了,前面的发言完全没有意义了

1. 你的不认为这是严重漏洞,是建立在自己修复过漏洞的CI框架,不是原生的CI框架

2. 你自己都承认了CI缓存漏洞,承认了其危害性,不然干嘛手工认证“有效性”?

3. 你说的“有效性”如果包括segment_array()长度有效性,那就确认了第2点

4. 任何人都可以轻松搞垮服务器的漏洞,如果不算严重漏洞,那这世界上就没有严重漏洞了
发表于 2009-4-7 11:15:01 | 显示全部楼层
那楼主觉得应该怎么验证有效性呢?希望能提出有建设性的思路或者是代码。
我觉得缓存有效性验证没有统一的模式,无法完全由框架“自动”提供。
发表于 2009-4-7 21:37:30 | 显示全部楼层
本帖最后由 BillyFan 于 2009-4-7 21:53 编辑
说起来是很容易,你有没有想过一个project每个method的segment验证是千变万化的?

“一条小小的验证”在一个project里面会变成“几十几百条小小的验证”

就拿我正在写的交友系统,粗略算一下带缓存的method少说也近百了,

如果给每个method增加小小的验证,那程序将耦合到囧rz的地步,


个人认为,就因为每个method里需要的验证都是不同的,因此framework本身是不可能提供一个万能的验证功能的。

同时,作为程序员,有义务和责任对自己写的程序进行安全验证,

举例来说,如果我在noticeboard这个controller里有如下一个method,
PHP复制代码
 
    function readpost($pid){
                $query = $this->db->get_where('posts',array('post_id' => $pid));
                $rowPost = $query->row();
                $data['content'] = $rowPost->title;
                $this->output->cache(10);
                $this->load->view('readpost',$data);
               
    }
 
复制代码

那么我想我有必要在这个method里增加一个验证,如果请求的$pid不存在的,应该给用户返回一个错误信息,于是乎,我就可以把method修改成下面这个样子:
PHP复制代码
 
    function readpost($pid){
                $query = $this->db->get_where('posts',array('post_id' => $pid));
                if($query->num_rows() == 1)
                    $rowPost = $query->row();
                    $data['content'] = $rowPost->title;
                     $this->output->cache(10);
                    $this->load->view('readpost',$data);
                }
                else{
                    // go to 404;
                }
    }
 
复制代码


这样,假设我的数据表post里只有id=1和id=2两条记录,
那么当用户请求:http://testdomain.com/noticeboard/readpost/3
的时候,就会返回404,而不生成缓存
发表于 2009-4-7 21:54:27 | 显示全部楼层
lz是否认为,在上面我的那个例子里,我加的那段验证是应该由CI自动完成,而不是我们程序员自己手动添加呢?

以我看来,在每一个这类应用中,这样的验证都应该由我们程序员来手动添加,而不能依靠CI,也无法依靠CI,
对于上面那个例子来说,如果是在加入验证之前的代码,当用户访问http://testdomain.com/noticeboard/readpost/3 的时候,由于post3根本不存在,那么$query->num_rows()就是0,而$query->row()的结果就不是一个object,$data['content'] = $rowPost->title;就会出错,这样的代码,恐怕在任何一个framework里都是报错吧?

无论是否有缓存,我们都需要在这里加一个有效性的验证,那就不存在为了缓存而额外加有效性验证的问题,

另外,lz提到
如果给每个method增加小小的验证,那程序将耦合到囧rz的地步,

我上面那个例子里,加了验证,没有增加耦合吧?

对于
你可以说你为每个method加验证,可是你终究会有疏忽的时候,而那时候你就要为CI的缺失买单了

这个。。。。。既然我们没有忘记加“$this->output->cache(10);”,为什么会忘记加验证呢?这个是我们自己的疏忽,不能怪CI吧,呵呵。
发表于 2009-4-7 22:07:42 | 显示全部楼层
当然,不可否认CI的这个缓存有点弱,
不知道是我没找到,还是CI的缓存就只有$this->output->cache(n);这么一个语句。
我发现,CI生成缓存文件的名字,就是简单的把用户请求的URL给md5一下,而不是由程序员自己来决定缓存的名字,
这样带来的不良结果是什么呢,还请看我前面那个例子,在我加了验证之后的代码:
PHP复制代码
 
function readpost($pid){
                $query = $this->db->get_where('posts',array('post_id' => $pid));
                if($query->num_rows() == 1)
                    $rowPost = $query->row();
                    $data['content'] = $rowPost->title;
                    $this->output->cache(10);
                    $this->load->view('readpost',$data);
                }
                else{
                    // go to 404;
                }
    }
 
复制代码

当用户请求http://testdomain.com/noticeboard/readpost/3 的时候,不生成缓存,
请求http://testdomain.com/noticeboard/readpost/1 的时候,就生成缓存,
看起来已经解决了缓存攻击的问题,
实际上呢,
如果我们请求一下
http://testdomain.com/noticeboard/readpost/1/2。。。。
omg,又一个缓存
再来,
http://testdomain.com/noticeboard/readpost/1/2/3。。。。
又一个缓存
只要第一个参数有效了,验证就通过,缓存就出现。。。。。
咋儿办?
我目前使用的解决方法是,读一个额外的参数:
PHP复制代码
 
function readpost($pid,$url_extra = false){
                $query = $this->db->get_where('posts',array('post_id' => $pid));
                if($query->num_rows() == 1)
                    $rowPost = $query->row();
                    $data['content'] = $rowPost->title;
                    if($url_extra === false){
                        $this->output->cache(10);
                    }
                    $this->load->view('readpost',$data);
                }
                else{
                    // go to 404;
                }
    }
 
复制代码

貌似很土很麻烦,每一个生成缓存的function里都要多增加一个额外的参数,
我还没想到其他的方法,希望有大侠能研究看看有没有更好的方法
 楼主| 发表于 2009-4-8 06:59:13 | 显示全部楼层
个人认为,就因为每个method里需要的验证都是不同的,因此framework本身是不可能提供一个万能的验证功能的。

同时,作为程序员,有义务和责任对自己写的程序进行安全验证,

举例来说,如果我在noticeboard这个co ...
BillyFan 发表于 2009-4-7 21:37


如果你没有自己修补过CI缓存的话,你的这个method明显存在漏洞,

如何攻击?只要这样,循环访问:
http://testdomain.com/noticeboard/readpost/2/1

http://testdomain.com/noticeboard/readpost/2/10000000

就会生成一千万个垃圾缓存

你写的method没有任何防御措施,也不会显示404

因为确定了$pid=2是合法的,但后面的/1~/10000000却没有检测,

写缓存的时候CI仍然会把/1~/10000000一起md5,生成1千万个垃圾缓存
 楼主| 发表于 2009-4-8 07:04:48 | 显示全部楼层
当然,不可否认CI的这个缓存有点弱,
不知道是我没找到,还是CI的缓存就只有$this->output->cache(n);这么一个语句。
我发现,CI生成缓存文件的名字,就是简单的把用户请求的URL给md5一下,而不是由程序员自己来决定 ...
BillyFan 发表于 2009-4-7 22:07


这个方法不好,这样会加重编程的负担,每个cache method都添加$extra===false的话,会造成紧耦合,不利于后期维护和再开发

目前比较有效且是松耦合的解决办法是扩展CI_URI和CI_Output,
我在前面已经提过思路,实现也很简单。

乃们包括斑竹都不愿意自己写代码哦?嘿嘿,那我整理一下发上来吧

本版积分规则