深入分析CSRF攻击方式与防御教程

 

CSRF是什么?

CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF。

 

CSRF可以做什么?

你这可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义发送恶意请求。CSRF能够做的事情包括:以你名义发送邮件,发消息,盗取你的账号,甚至于购买商品,虚拟货币转账......造成的问题包括:个人隐私泄露以及财产安全。

 

CSRF漏洞现状

CSRF这种攻击方式在2000年已经被国外的安全人员提出,但在国内,直到06年才开始被关注,08年,国内外的多个大型社区和交互网站分别爆出CSRF漏洞,如:NYTimes.com(纽约时报)、Metafilter(一个大型的BLOG网站),YouTube和百度HI......而现在,互联网上的许多站点仍对此毫无防备,以至于安全业界称CSRF为“沉睡的巨人”。

 

CSRF的原理

下图简单阐述了CSRF攻击的思想:

csrfyl

从上图可以看出,要完成一次CSRF攻击,受害者必须依次完成两个步骤:

登录受信任网站A,并在本地生成Cookie。
在不登出A的情况下,访问危险网站B。
看到这里,你也许会说:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。是的,确实如此,但你不能保证以下情况不会发生:

你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。
你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。(事实上,关闭浏览器不能结束一个会话,但大多数人都会错误的认为关闭浏览器就等于退出登录/结束会话了......)
上图中所谓的攻击网站,可能是一个存在其他漏洞的可信任的经常被人访问的网站。
上面大概地讲了一下CSRF攻击的思想,下面我将用几个例子详细说说具体的CSRF攻击,这里我以一个银行转账的操作作为例子(仅仅是例子,真实的银行网站没这么傻:>)

 

示例

示例1

银行网站A,它以GET请求来完成银行转账的操作,如:http://www.111cn.net /Transfer.php?toBankId=11&money=1000

危险网站B,它里面有一段HTML的代码如下:

XHTML

<img src=http://www.111cn.net /Transfer.php?toBankId=11&money=1000>

首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块......

为什么会这样呢?原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中的<img>以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源“http://www.111cn.net /Transfer.php?toBankId=11&money=1000”,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作......

 

示例2

为了杜绝上面的问题,银行决定改用POST请求完成转账操作。

银行网站A的WEB表单如下:

XHTML

<form action="Transfer.php" method="POST">
    <p>ToBankId: <input type="text" name="toBankId" /></p>
    <p>Money: <input type="text" name="money" /></p>
    <p><input type="submit" value="Transfer" /></p>
</form>
后台处理页面Transfer.php如下:

PHP

<?php
    session_start();
    if (isset($_REQUEST['toBankId'] && isset($_REQUEST['money']))
    {
        buy_stocks($_REQUEST['toBankId'], $_REQUEST['money']);
    }
?>
危险网站B,仍然只是包含那句HTML代码:

XHTML

<img src=http://www.111cn.net /Transfer.php?toBankId=11&money=1000>

和示例1中的操作一样,你首先登录了银行网站A,然后访问危险网站B,结果.....和示例1一样,你再次没了1000块~T_T,这次事故的原因是:银行后台使用了$_REQUEST去获取请求的数据,而$_REQUEST既可以获取GET请求的数据,也可以获取POST请求的数据,这就造成了在后台处理程序无法区分这到底是GET请求的数据还是POST请求的数据。在PHP中,可以使用$_GET和$_POST分别获取GET请求和POST请求的数据。在JAVA中,用于获取请求数据request一样存在不能区分GET请求数据和POST数据的问题。

 
示例3

经过前面2个惨痛的教训,银行决定把获取请求数据的方法也改了,改用$_POST,只获取POST请求的数据,后台处理页面Transfer.php代码如下:

<?php
    session_start();
    if (isset($_POST['toBankId'] && isset($_POST['money']))
    {
        buy_stocks($_POST['toBankId'], $_POST['money']);
    }
?>
然而,危险网站B与时俱进,它改了一下代码:
<html>
  <head>
    <script type="text/javascript">
      function steal()
      {
               iframe = document.frames["steal"];
               iframe.document.Submit("transfer");
      }
    </script>
  </head>
 
  <body onload="steal()">
    <iframe name="steal" display="none">
      <form method="POST" name="transfer" action="http://www.111cn.net /Transfer.php">
        <input type="hidden" name="toBankId" value="11">
        <input type="hidden" name="money" value="1000">
      </form>
    </iframe>
  </body>
</html>
如果用户仍是继续上面的操作,很不幸,结果将会是再次不见1000块......因为这里危险网站B暗地里发送了POST请求到银行!

总结一下上面3个例子,CSRF主要的攻击模式基本上是以上的3种,其中以第1,2种最为严重,因为触发条件很简单,一个<img>就可以了,而第3种比较麻烦,需要使用JavaScript,所以使用的机会会比前面的少很多,但无论是哪种情况,只要触发了CSRF攻击,后果都有可能很严重。

理解上面的3种攻击模式,其实可以看出,CSRF攻击是源于WEB的隐式身份验证机制!WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的!

 

CSRF的防御

我总结了一下看到的资料,CSRF的防御可以从服务端和客户端两方面着手,防御效果是从服务端着手效果比较好,现在一般的CSRF防御也都在服务端进行。

 

服务端进行CSRF防御

服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数或检测访问来路是否可信。

 

Cookie Hashing(所有表单都包含同一个伪随机值)

第一个方案可能是解决这个问题的最简单和快捷的方案了,因为攻击者不能够获得被攻击者的Cookies内容,也就不能够构造相应的表单。

<?php
    //构造加密的Cookie信息
    $value = "DefenseSCRF";
    setcookie("cookie", $value, time()+3600);
?>
在表单里增加Hash值,以认证这确实是用户发送的请求。

<?php
    $hash = md5($_COOKIE['cookie']);
?>
<form method=”POST” action=”transfer.php”>
    <input type=”text” name=”toBankId”>
    <input type=”text” name=”money”>
    <input type=”hidden” name=”hash” value=”<?=$hash;?>”>
    <input type=”submit” name=”submit” value=”Submit”>
</form>

然后在服务器端进行Hash值验证

<?php
        if(isset($_POST['check'])) {
             $hash = md5($_COOKIE['cookie']);
             if($_POST['check'] == $hash) {
                  doJob();
             } else {
        //...
             }
        } else {
      //...
        }
?>
事实上,如果我们不考虑用户的Cookies很容易由于网站中存在XSS漏洞而被偷窃(我们已经知道这样的事情并不少见)这一事实,这是一个很好的应对对CSRF的解决方案。如果我们为用户的每一个表单请求中都加入随机的Cookies,那么这种方法会变得更加安全,但是这并不是十分合适。

 
HTTP来路

检测访问来路是否可信的最简单方法是,获得HTTP请求中的来路信息(即名为Referer的HTTP头)并且检查它来自站内还是来自一个远程的恶意页面:这是一个很好的解决方法,但是由于可以对服务器获得的请求来路进行欺骗以使得他们看起来合法,但这种方法不能够有效防止攻击。

让我们来看看为什么这并不是一个合适的方法。下面的代码展示了HTTP Referer实现方法的一个例子:

check.php

if(eregi("www.111cn.net", $_SERVER['HTTP_REFERER'])) {
    do_something();
} else {
    echo "Malicious Request!";
}
这个检测则会轻易的忽略掉来自某个攻击者伪造的HTTP Referer欺骗,攻击者可以使用如下代码:

header("Referer: www.111cn.net");

或者其他在恶意脚本中伪造HTTP头并发送的方法。

由于HTTP Referer是由客户端浏览器发送的,而不是由服务器控制的,因此你不应当将该变量作为一个信任源。

 

验证码

另外一个解决这类问题的思路则是在用户提交的每一个表单中使用一个随机验证码,让用户在文本框中填写图片上的随机字符串,并且在提交表单后对其进行检测。

这个方法曾经在之前被人们放弃,这是由于验证码图片的使用涉及了一个被称为MHTML的Bug,可能在某些版本的微软IE中受影响。

你可以在Secunia的站点上获得关于此缺陷的详细信息:http://secunia.com/advisories/19738/ 。

这里是Secunia关于此Bug解释的概述:

“此缺陷是由于处理“mhtml:”的URL处理器重定向引起的。它可以被用来利用从另外一个网站访问当前的文档”

事实上,我们知道,这个Bug已经被微软放出的Windows XP和Windows Vista及其浏览器IE6.0的修复包所解决了。即使他的确出现了安全问题,这么长时间也会有其他的可靠方案出现。

 
一次性令牌

现在让我们来看经过研究,我希望介绍的最后一种解决方案:在使用这些不可靠的技术后,我尝试做一些不同然而却是更有效的方法。

为了防止Web表单受到Session欺骗(CSRF)的攻击,我决定检测可能被伪装或伪造的每一个项目。因此我需要来创造一次性令牌,来使得在任何情况下都不能够被猜测或者伪装,这些一次性令牌在完成他们的工作后将被销毁。

让我们从令牌值的生成开始:

functions.php

function gen_token() {
     // uniqid()根据当前的时间(毫秒数)获得一个唯一的ID,这个唯一ID有利于生成一个不重复的数值
     $hash = md5(uniqid(rand(), true));
 
     // 得到相应ID值的MD5散列,而后我们从该散列中以一个小于24的数字为开始位置,选取8位字母
     $n = rand(1, 24);
     $token = substr($hash, $n, 8);
 
     return $token;
}
现在让我们生成一个Session令牌,在稍后的检查中我们会用到它。

functions.php

function gen_stoken() {
     // Call the function to generate the token
     $token = gen_token();
     // Destroy any eventually Session Token variable
     destroy_stoken();
     // Create the Session Token variable
     session_register(STOKEN_NAME);
     $_SESSION[STOKEN_NAME] = $token;
}
在这个函数中我们调用gen_token()函数,并且使用返回的令牌将其值复制到一个新的$_SESSION变量。

现在让我们来看启动完整机制中为我们的表单生成隐藏输入域的函数:

functions.php

function gen_input() {
     // Call the function to generate the Session Token variable
     gen_stoken();
     // Generate the form input code
     echo '<input type="hidden” name=”' . FTOKEN_NAME . '” value=”' . $_SESSION['STOKEN_NAME'] . '">';
}
我们可以看到,这个函数调用了gen_stoken()函数并且生成在WEB表单中包含隐藏域的HTML代码。

接下来让我们来看实现对隐藏域中提交的Session令牌的检测的函数:

functions.php

function token_check() {
     // Check if the Session Token exists
     if (is_stoken()) {
          // Check if the request has been sent
          if(isset($_REQUEST[FTOKEN_NAME])) {
               // If the Form Token is different from Session Token
               // it’s a malicious request
               if($_REQUEST[FTOKEN_NAME] != $_SESSION[STOKEN_NAME]) {
                    gen_error(1);
                    destroy_stoken();
                    exit();
               } else {
                    destroy_stoken();
               }
          // If it isn’t then it’s a malicious request
          } else {
               gen_error(2);
               destroy_stoken();
               exit();
          }
     // If it isn’t then it’s a malicious request
     } else {
          gen_error(3);
          destroy_stoken();
          exit();
     }
}
这个函数检测了$_SESSION[STOKEN_NAME]和$_REQUEST[FTOKEN_NAME]的存在性(我使用了$_REQUEST方法来使得GET和POST两种方式提交的表单变量均能够被接受),而后检测他们的值是否相同,因此判断当前表单提交是否是经过认证授权的。

这个函数的重点在于:在每次检测步骤结束后,令牌都会被销毁,并且仅仅在下一次表单页面时才会重新生成。

这些函数的使用方法非常简单,我们只需要加入一些PHP代码结构。

下面是Web表单:

form.php

<?php
     session_start();
     include("functions.php");
?>
<form method="POST" action="resolve.php">
     <input type="text" name="first_name">
     <input type="text" name="last_name">
     <!– Call the function to generate the hidden input –>
     <? gen_input(); ?>
     <input type="submit" name="submit" value="Submit">
</form>
下面是resolve.php的脚本代码:

resolve.php

<?php
session_start();
include("functions.php");
 
// Call the function to make the check
token_check();
 
// Your code
你可以看到,实现这样一个检测是十分简单的,但是它可以避免你的用户表单被攻击者劫持,以避免数据被非法授权。

 

总结

现在你的Web应用程序没有百分百的安全,但是你可以开始避免绝大多数普通的攻击技术。

我希望您关注的另一个要点是,Web开发者不应当忽视一般的程序错误(例如XSS 浏览器漏洞等等),不将这些考虑为对您的用户的潜在威胁是一个巨大的错误:你应该永远记得它们将影响程序的信任性、安全性和互操作性

时间: 2024-08-27 09:23:05

深入分析CSRF攻击方式与防御教程的相关文章

学习手册:DDoS的攻击方式及防御手段

现如今,信息技术的发展为人们带来了诸多便利,无论是个人社交行为,还是商业活动都开始离不开网络了.但是网际空间带来了机遇的同时,也带来了威胁,其中DDoS就是最具破坏力的攻击,通过这些年的不断发展,它已经成为不同组织和个人的攻击,用于网络中的勒索.报复,甚至网络战争. 先聊聊DDoS的概念和发展 在说发展之前,咱先得对分布式拒绝服务(DDoS)的基本概念有个大体了解. 啥叫"拒绝服务"攻击呢? 其实可以简单理解为:让一个公开网站无法访问.要达到这个目的的方法也很简单:不断地提出服务请求,

Web安全之CSRF攻击

CSRF是什么? CSRF(Cross Site Request Forgery),中文是跨站点请求伪造.CSRF攻击者在用户已经登录目标网站之后,诱使用户访问一个攻击页面,利用目标网站对用户的信任,以用户身份在攻击页面对目标网站发起伪造用户操作的请求,达到攻击目的. 举个例子 简单版: 假如博客园有个加关注的GET接口,blogUserGuid参数很明显是关注人Id,如下: http://www.cnblogs.com/mvc/Follow/FollowBlogger.aspx?blogUse

《XSS跨站脚本攻击剖析与防御》—第6章6.1节参 考 文 献

参 考 文 献 XSS跨站脚本攻击剖析与防御 [1] <精通 JavaScript> (美)John Resi /陈贤安(译) [2] <JavaScript核心技术> (美)Shelley Powers /苏敬凯(译) [3] <XSS Attacks: Cross Site Scripting Exploits and Defense>Seth Fogie/Jeremiah Grossman/Robert Hansen/Anton Rager/Petko D. Pe

《XSS跨站脚本攻击剖析与防御》—第6章6.5节利用Flash进行CSRF

6.5 利用Flash进行CSRFXSS跨站脚本攻击剖析与防御Flash在客户端提供了两个控制属性:allowScriptAccess属性和allowNetworking属性,其中AllowScriptAccess控制Flash与HTML页面的通信,如果设置不恰当会导致XSS:而AllowNetworking控制Flash与外部网络的通信,如果设置不当会导致CSRF. CSRF的中文名称是"跨站请求伪造",和XSS一样是一种常见的Web程序漏洞(攻击手段),CSRF能够做的事情是以你的

深入分析DDoS攻击防御的原理

1. DDoS攻击基础DDoS(Distributed Denial of Service,分布式拒绝服务)攻击的主要目的是让指定目标无法提供正常服务,甚至从互联网上消失,是目前最强大.最难防御的攻击之一. 按照发起的方式,DDoS可以简单分为三类. 第一类以力取胜,海量数据包从互联网的各个角落蜂拥而来,堵塞IDC入口,让各种强大的硬件防御系统.快速高效的应急流程无用武之地.这种类型的攻击典型代表是ICMP Flood和UDP Flood,现在已不常见. 第二类以巧取胜,灵动而难以察觉,每隔几分

预防CSRF攻击

什么是CSRF CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSRF/XSRF. 那么CSRF到底能够干嘛呢?你可以这样简单的理解:攻击者可以盗用你的登陆信息,以你的身份模拟发送各种请求.攻击者只要借助少许的社会工程学的诡计,例如通过QQ等聊天软件发送的链接(有些还伪装成短域名,用户无法分辨),攻击者就能迫使Web应用的用户去执行攻击者预设的操作.例如,当用户登录网络银

xss 和 csrf攻击详解

在那个年代,大家一般用拼接字符串的方式来构造动态 SQL 语句创建应用,于是 SQL 注入成了很流行的攻击方式.在这个年代, 参数化查询 已经成了普遍用法,我们已经离 SQL 注入很远了.但是,历史同样悠久的 XSS 和 CSRF 却没有远离我们.由于之前已经对 XSS 很熟悉了,所以我对用户输入的数据一直非常小心.如果输入的时候没有经过 Tidy 之类的过滤,我一定会在模板输出时候全部转义.所以个人感觉,要避免 XSS 也是很容易的,重点是要"小心".但最近又听说了另一种跨站攻击 C

《XSS跨站脚本攻击剖析与防御》目录—导读

内容提要 XSS跨站脚本攻击剖析与防御 本书是一本专门剖析XSS安全的专业书,总共8章,主要包括的内容如下. 第1章 XSS初探,主要阐述了XSS的基础知识,包括XSS的攻击原理和危害.第2章 XSS利用方式,就当前比较流行的XSS利用方式做了深入的剖析,这些攻击往往基于客户端,从挂马.窃取Cookies.会话劫持到钓鱼欺骗,各种攻击都不容忽视.第3章 XSS测试和利用工具,介绍了一些常见的XSS测试工具.第4章 发掘XSS漏洞,着重以黑盒和白盒的角度介绍如何发掘XSS漏洞,以便帮助读者树立安全

《XSS跨站脚本攻击剖析与防御》—第6章6.4节利用Flash进行XSS攻击剖析

6.4 利用Flash进行XSS攻击剖析 XSS跨站脚本攻击剖析与防御 利用嵌入Web页面中的Flash进行XSS有一个决定因素:allowScriptAccess属性.allowScriptAccess是使用或 下面是一个简单的示例: allowScriptAccess属性控制着Flash与HTML页面的通信,可选的值有3个: always:允许随时执行脚本操作 never:禁止所有脚本执行操作 samedomain:只有在Flash 应用程序来自与HTML页相同的域时才允许执行脚本操作 其属