yuzhigang5460 发表于 2014-12-27 01:22:51

CI的CSRF防范原理及注意事项

本帖最后由 yuzhigang5460 于 2015-3-24 21:32 编辑

首先我们谈谈什么是CSRF,它就是Cross-Site Request Forgery跨站请求伪造的简称。很多开发者甚至不够重视这个问题,认为这不是安全漏洞,而不过是恶意访问而已,它的攻击原理我在这里简单地描述一下:

有一天你打开你简单优雅逼格十足的谷歌浏览器,首先打开了一个tab页,登录并访问了你的微博首页。我们这里假设weibo.cn有这样一个网址: http://www.weibo.cn?follow_uid=123 ,意思是关注id为123的一个用户。这是一个正常的地址,访问也没有问题。
紧接着你的QQ群里发来了一个让你感到好奇的链接,http://www.comeonbaby.com, 你禁不住诱惑打开了这个链接,并在浏览器里的另一个tab页里显示出来。紧接着,你打开你的微博tab页,发现无故关注了一个新的用户。咦,这是为何?

原因很简单,很可能在你打开的http://www.comeonbaby.com链接里存在着这样一个html元素: <img src="http://www.weibo.cn?follow_uid=123" alt="" />, 浏览器试图加载这个img,很显然加载失败了,因为它不是一个有效的图片格式。但是,这个请求依然被发送出去了,此时你的微博是登录状态中,然后,你就真的follow了123, 你看,你被强奸了。

这就是简单的csrf攻击。

在实际的网站项目中,如: http://www.abc.com/logout之类的链接都应该注意,注销类的、删除内容类的、转账类的都可能中埋伏,轻则让你感到诧异,重则数据丢失,财产损失,所以要重视任何一个对数据有操作行为的url。那么我们在CI里如何解决呢?
简单地:
第一步: 在application/config/config.php里配置以下字段:

$config['csrf_protection'] = true;
$config['csrf_token_name'] = 'csrf_token_name';
$config['csrf_cookie_name'] = 'csrf_cookie_name';
$config['csrf_expire'] = 7200;

第二步: 在form里使用form_open(),帮助生成一个token。

接下来我说一下csrf的工作原理:

简单地来说,当我们访问一个页面如: http://www.abc.com/register时, CI会生成一个名为csrf_cookie_name的cookie,其值为hash,并发送到客户端。同时由于你在该页面里使用了form_open(),会在form标签下生成一个<input type="hidden" name="csrf_token_name" value="12uffu2910"/>之类的隐藏字段,其值也为hash。

紧接着用户点击了注册按钮,浏览器将这些数据包括csrf_token_name发送到(post到)服务器,同时也会将名为csrf_cookie_nam的cookie发送回去。服务器会比较csrf_token_name的值(也就是hash) 与 csrf_cookie_name 的cookie值(同样也是hash)是否相同, 如果相同则通过,如果不同则说明是csrf攻击。

接下来我们分析一下CI的源代码:

CI在Codeigniter.php里会先加载Security类,接着加载Input类,这两个类在每次访问时都会自动加载的。

先加载 Security类,该类的初始方法首先设置一个hash, 这个hash如果为空,则会在cookie里检查是否存在,如果存在则设为hash;否则会计算出一个新的hash。

接下来开始初始化Input类,导致调用$this->security->csrf_verify()方法。该方法首先判断该请求是否为post请求,如果不是,则会设置一个名为csrf_cookie_name的cookie,其值为hash,如果是post请求,则会用post过来的csrf_token值与csrf_cookie值比较,比较失败则输出错误;成功则会删除csrf_tookie的值,再次设置csrf_hash的值(同上,先检查cookie,此时为空,则会新计算一个hash),紧接着又重新赋予了新的csrf_cookie值。

在实际操作过程中, 如有一个register的视图,其页面必然后 form_open()的调用,该方法会产生一个 csrf_token的 hash值, 当post到一个 action时, 该action自然就会执行检查。

由上可以知道:
(1)如果开启了csrf保护,每次调用都会生成一个叫csrf_cookie_name的cookie, 并将值设为hash;
(2)直到遇到一次post请求时才会将以前的cookie删除,重新生成一个hash, 如此反复。

但是,…………

细心的读者可能发现了, 我上文中举的例子是get请求,而CI的csrf只是设计了post请求的防范策略,那么请你想想,你在你的项目中是否存在着 get请求的 资源操作url地址呢?你是否对这样的url地址进行过csrf防范?

我们的建议:
(1)重要的资源操作,都尽量采取post请求,防止csrf攻击;
(2)如果你执意使用get请求,也不是没有办法,原理跟上面也是类似的,比如上文提到的关注账号的操作,你可以设计这样一个地址:
http://www.weibo.cn?follow_uid=123&token=73ksdkfu102
token是什么?是你随机产生的一个字符串,等用户发送回去后你依然做验证,如果验证通过,则执行后续的关注操作,如果没通过,我们就认为该操作是不合法的。 那个诱惑你的攻击者不可能知道每个人的token, 即使你点击了那个链接,依然不会被认为是有效的访问地址。

一点建议:
由于CI开启csrf保护是全局性的,这样就会导致你的任何post请求都需要加入csrf_token_name的数据字段,的确非常繁琐,有些人索性就关闭了。在这里给出三个解决方法:
(1)每个form里都加入这样一个传递数据:
    $.post(url, {'<?php echo $this->security->get_csrf_token_name(); ?>' : '<?php echo $this->security->get_csrf_hash(); ?>'}, function(){});
(2)为ajax请求加入全局传递数据:
//
$(function($) {
    // this script needs to be loaded on every page where an ajax POST may happen
    $.ajaxSetup({
      data: {
            '<?php echo $this->security->get_csrf_token_name(); ?>' : '<?php echo $this->security->get_csrf_hash(); ?>'
      }
    });
// now write your ajax script
});
(3)自己写一个helper方法,直接在view中使用,加入隐藏字段,如果你不喜欢使用form_open()的话:
    function csrf_hidden(){
   $ci = &get_instance();
   $name = $ci->security->get_csrf_token_name();
   $val = $ci->security->get_csrf_hash();
   echo "<input type=\"hidden\" name=\"$name\" value=\"$val\" />";
    }

上文可能有写的不对的地方,还请指正。http://www.ifixedbug.com/posts/codeigniter-csrf-story



startbbs 发表于 2014-12-27 10:19:59

写得不错,得顶.

caoniao 发表于 2014-12-27 12:42:12

支持 csrf中的非法http请求携带了普通用户的cookie信息,因此是有效的,执行了普通用户不想进行的动作

一叶扁舟 发表于 2014-12-29 08:04:34

{:soso_e179:}赞一个

bob 发表于 2015-1-15 08:41:18

学习了,谢谢分享

0Shaka 发表于 2015-2-10 00:59:28

这篇文章这么点儿回复不科学啊

john11 发表于 2015-3-24 12:15:57

{:soso_e179:}学习了。好文章。

鹿人甲 发表于 2016-9-19 15:44:37

写得很不错

ci_zfy 发表于 2016-11-21 11:23:53

请问楼主,关闭全局的csrf验证($config['csrf_protection'] = FALSE;)后,,在表单中手动加入{ '<?php echo $this->security->get_csrf_token_name(); ?>' : '<?php echo $this->security->get_csrf_hash(); ?>' }后提交,CI会执行csrf验证吗?
页: [1]
查看完整版本: CI的CSRF防范原理及注意事项