欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

PHP代码审计:CSRF漏洞

程序员文章站 2022-04-30 10:23:16
...

当你的才华

还撑不起你的野心时

那你就应该静下心来学习


      代码审计学习线上实验,都是CE一边实操,一边整理的笔记,方便以后翻看时,可快速查阅。

目录

CSRF漏洞审计

CSRF 与XSS 区别

CSRF攻击流程

CSRF 漏洞实例

CSRF 漏洞防御


CSRF漏洞审计

简介:

       CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性

       跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并执行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)。由于浏览器曾经认证过,所以被访问的网站会认为是真正的用户操作而去执行。这利用了web中用户身份验证的一个漏洞:简单的身份验证只能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的

CSRF 与XSS 区别:

       • XSS: XSS漏洞——构造payload——发送给受害人——受害人点击打开——攻击者获取受害人的cookie——攻击者使用受害人cookie完成攻击

       • CSRF: CSRF漏洞——构造payload——发送给受害人——受害人点击打开——受害人执行代码——受害人完成攻击(不知情)

 

CSRF攻击流程: 

PHP代码审计:CSRF漏洞

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

  • 登录受信任网站A,并在本地生成Cookie

  • 在不退出A的情况下,访问危险网站B

假设:“如果我不满足以上两个条件中的一个,我就不会受到CSRF的攻击”。

是的,确实如此,但你不能保证以下情况不会发生:

  • 你不能保证你登录了一个网站后,不再打开一个tab页面并访问另外的网站。

  • 你不能保证你关闭浏览器了后,你本地的Cookie立刻过期,你上次的会话已经结束。

所以 CSRF 是一种较难防御、又危险极大的漏洞

 

CSRF 漏洞实例

       当我们打开或者登陆某个网站的时候,浏览器与网站所存放的服务器将会产生一个会话(cookies),在这个会话没有结束时,你就可以利用你的权限对网站进行操作。然而,攻击者就是利用这个特性,让受害者触发我们构造的表单或者语句,然后达到攻击者想要达到的目的

文件里包含如下文件 adduser.php 、catuser.php 、index.html 、login.php 、user.txt 、WebB.php

代码如下,变量 $XssReflex 获取 get 方式传递的变量名为 input 的变量值(值为一个字符串),然后直接通过echo()函数输出,注意这中间并未对用户输入进行任何过滤

index.html

  1. <!DOCTYPE html>
  2. <html>
  3. <head><meta charset="utf-8">
  4. <title>CSRF演示</title>
  5. </head>
  6. <body>
  7. <form name="LoginForm" method="post" action="login.php" onSubmit="return InputCheck(this)">
  8. <p>
  9. <label for="username" class="label">用户名:</label>
  10. <input id="username" name="username" type="text" class="input" />
  11. <p/>
  12. <p>
  13. <label for="password" class="label">密 &nbsp;&nbsp;&nbsp;码:</label>
  14. <input id="password" name="password" type="password" class="input" />
  15. <p/>
  16. <p>
  17. <input type="submit" name="submit" value=" 确 定 " class="left" />
  18. </p>
  19. </form>
  20. </body>
  21. </html>

login.php

  1. <?php
  2. header("Content-Type: text/html;charset=utf-8");
  3. session_start();
  4. $csrf_token = md5(uniqid());
  5. $_SESSION['csrf_token'] = $csrf_token;
  6. if($_GET['action'] == "logout") {
  7. unset($_SESSION['username']);
  8. echo '注销登录成功!点击此处 <a href="index.html">登录</a>';
  9. exit;
  10. }
  11. if(!isset($_POST['submit'])){
  12. exit("非法访问!");
  13. }
  14. $username = $_POST['username'];
  15. $password = $_POST['password'];
  16. if($username == 'admin' && $password == 'admin') {
  17. $_SESSION['username'] = $username;
  18. $_SESSION['password'] = $password;
  19. echo $username, '欢迎您!<br />登录成功获得如下功能: <br /><a href="adduser.php">1.添加用户</a><br/>';
  20. echo '<a href="catuser.php">2.查看用户</a><br/>';
  21. echo '<a href="login.php?action=logout">3.注销</a>登录<br/>';
  22. } else {
  23. exit('登录失败!点击此处 <a href="index.html">返回</a>');
  24. }
  25. ?>

adduser.php

  1. <?php
  2. header("Content-Type: text/html;charset=utf-8");
  3. session_start();
  4. if($_SESSION['username'] != 'admin') {
  5. echo 'Error!';
  6. header("Location:index.html");
  7. exit;
  8. }
  9. $log=fopen("user.txt","a");
  10. fwrite($log,'username:ce'."\r\n");
  11. fwrite($log,'password:ce'."\r\n");
  12. echo '<br />';
  13. fclose($log);
  14. //echo '<a href="login.php">点击返回</a><br/>';
  15. ?>

catuser.php

  1. <?php
  2. header("Content-Type: text/html;charset=utf-8");
  3. session_start();
  4. if($_SESSION['username'] != 'admin') {
  5. echo 'Error!';
  6. header("Location:index.html");
  7. exit;
  8. }
  9. if(file_exists("user.txt"))
  10. {
  11. $read= fopen("user.txt",'r');
  12. while(!feof($read))
  13. {
  14. echo fgets($read)."</br>";
  15. }
  16. fclose($read);
  17. }
  18. ?>

WebB.php

  1. <!DOCTYPE html>
  2. <html>
  3. <head><meta charset="utf-8">
  4. <title>Hack</title>
  5. </head>
  6. <body>
  7. <a href="http://localhost/csrf/adduser.php">恶意链接</a>
  8. </body>
  9. </html>

最后创建一个空的user.txt

 

思考一下CSRF攻击和XSS攻击原理上的区别,对比着学习会更加容易理解。

模拟网站后台管理的页面,用户名、密码都是admin

PHP代码审计:CSRF漏洞

登录成功页面:

PHP代码审计:CSRF漏洞

一般来说后台管理员都具有添加用户的功能,所以登录成功之后点击添加用户(这里为了方便演示,并没有让管理员自定义账户名、密码的功能,而是添加默认的账户(ce)和密码(ce),成功后页面不会回显,但实际已经添加成功,我们点击" 2.查看用户 "按钮,查看是否添加默认账户(ce)和密码(ce)

PHP代码审计:CSRF漏洞

这时候如果我们记录下刚刚添加用户的网页地址,是否无论是哪个用户,只要访问这个地址就能添加用户呢?

为了验证这个想法,接下来我们点击注销登录(清楚cookie信息):

尝试在浏览器输入之前添加用户的页面地址:localhost/csrf/adduser.php,尝试直接添加用户 

PHP代码审计:CSRF漏洞

但是并没有成功,页面也自动跳转到登录页面,为什么呢?

因为adduser.php页面需要验证session信息才能执行相应操作。

所以就有人想到:“既然我们自己不能成功访问这个页面,能否在管理员不知道的情况下,欺骗他访问这个页面呢?”。 我们再次使用admin用户登陆,并新建标签页。 这时假设管理员继续访问网站 B(可以与合法网站在不同服务器上) ,在新标签页里输入url

PHP代码审计:CSRF漏洞

页面B上面存在攻击者事先写好的恶意链接,并诱使我们点击(比如做成图片连接): 

再查看用户时,发现后台已经不知不觉添加上了用户:

PHP代码审计:CSRF漏洞

PHP代码审计:CSRF漏洞

这就是一次利用CSRF漏洞添加后台的实例, 只要用户(受害者)点击该链接,就完成了一次CSRF攻击,虽然用户可能本身并没有执行该操作的意图

 

CSRF 漏洞防御

在服务器端防御CSRF攻击主要有四种策略:

  • 验证HTTP Referer 字段

      根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址

      在通常情况下,访问一个安全受限页面的请求必须来自于同一个网站

      比如某银行的转账是通过用户访问http://bank.test/test?page=10&userID=101&money=10000页面完成,用户必须先登录bank.test,然后通过点击页面上的按钮来触发转账事件

      当用户提交请求时,该转账请求的Referer值就会是转账按钮所在页面的URL(本例中,通常是以bank. test域名开头的地址)

      而如果攻击者要对银行网站实施CSRF攻击,他只能在自己的网站构造请求,当用户通过攻击者的网站发送请求到银行时,该请求的Referer是指向攻击者的网站。因此,要防御CSRF攻击,银行网站只需要对于每一个转账请求验证其Referer值,如果是以bank. test开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果Referer是其他网站的话,就有可能是CSRF攻击,则拒绝该请求

  • 在请求地址中添加token并验证

      CSRF攻击之所以能够成功,是因为攻击者可以伪造用户的请求,该请求中所有的用户验证信息都存在于Cookie中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的Cookie来通过安全验证

      由此可知,抵御CSRF攻击的关键在于:在请求中放入攻击者所不能伪造的信息,并且该信息不存在于Cookie之中

      鉴于此,系统开发者可以在HTTP请求中以参数的形式加入一个随机产生的token,并在服务器端建立一个拦截器来验证这个token,如果请求中没有token或者token内容不正确,则认为可能是CSRF攻击而拒绝该请求

  • 在HTTP头中自定义属性并验证

      自定义属性的方法也是使用token并进行验证,和前一种方法不同的是,这里并不是把token以参数的形式置于HTTP请求之中,而是把它放到HTTP头中自定义的属性里

      通过XMLHttpRequest这个类,可以一次性给所有该类请求加上csrftoken这个HTTP头属性,并把token值放入其中。这样解决了前一种方法在请求中加入token的不便,同时,通过这个类请求的地址不会被记录到浏览器的地址栏,也不用担心token会通过Referer泄露到其他网站

  • 添加验证码并验证

我不需要*,只想背着她的梦

一步步向前走,她给的永远不重