【实用手册】CSRF跨站请求伪造漏洞
2023-4-23 00:23:11 Author: 利刃信安攻防实验室(查看原文) 阅读量:12 收藏

漏洞名称:

CSRF跨站请求伪造漏洞

漏洞级别:

中危

漏洞描述:

攻击者通过伪造请求,利用目标用户的权限在网站上执行非法操作。这种漏洞通常是通过在网站中嵌入恶意代码来实现的,当目标用户访问了恶意网站时,攻击者就可以通过该漏洞在目标用户的名义下进行攻击。

漏洞成因:

数据采用GET或POST请求,数据包中各参数均为用户可控参数,没有设置CSRF-Token,没有验证Referer字段,没有图形验证码和短信、邮件验证码,同时也没有校验非标准Http头部X-Requested-With: XMLHttpRequest,没有设置自定义的Http头部字段,以上条件均满足则可以判断网站存在CSRF漏洞。

漏洞危害:

在成功的CSRF攻击中,攻击者会有意识地引导受害用户执行一个操作,可能是更改他们账户上的电子邮件地址,更改他们的密码或进行资金转账。根据动作的性质,攻击者可能能够获得对用户帐户的完全控制。如果被攻击的用户在应用程序中拥有特权角色,那么攻击者可能能够完全控制应用程序的所有数据和功能。

修复建议:

1.使用CSRF-Token:业界一致的做法是使用一个CSRF-Token。

CSRF攻击之所以能够成功是因为攻击者可以伪造用户的请求,对此最好的防御手段就是让攻击者无法伪造这个请求。因此,我们可以在Http请求中(千万不要放在Cookie中)以参数的形式添加一个随机的CSRF-Token,并在服务端检查这个CSRF-Token是否正确,如不正确或不存在,则可以认为是不安全的请求,拒绝提供相关服务。

注意:

如果网站同时还存在XSS漏洞时,上述CSRF-Token的方法将可能失效,因为XSS可以模拟浏览器执行操作,攻击者通过XSS漏洞读取CSRF-Token值后,便可以构造出合法的用户请求了。所以在做好CSRF防护的同时,相应的安全防护也应做好。

2.验证Referer字段

在通常情况下,访问一个安全受限页面的请求来自于同一个网站,Http头部中的Referer字段记录了该Http请求的来源地址,如果Referer中的地址不是来源于本网站或不存在则可认为是不安全的请求,对于该请求应予以拒绝。这种方法简单易行,对于现有的系统只需在加上一个检查Referer值的过滤器,无需改变当前系统的任何已有代码和逻辑。

但是,这种方法存在一些问题需要考虑:首先,Referer的值是由浏览器提供的,虽然Http协议上有明确的要求,但是每个浏览器对于Referer的具体实现可能有差别,并且不能保证浏览器自身没有安全漏洞,将安全性交给第三方(即浏览器)保证,从理论上来讲是不可靠的;其次,用户可能会出于保护隐私等原因禁止浏览器提供Referer,这样的话正常的用户请求也可能因没有Referer信息被误判为不安全的请求,无法提供正常的使用。

注意:

如果网站同时还存在XSS漏洞时,上述方法将可能失效,因为XSS可以模拟浏览器执行操作,构造出合法的用户请求,这样Referer字段记录的依然是本网站的地址从而可以绕过Referer校验。所以在做好CSRF防护的同时,相应的安全防护也应做好。

3.添加验证码机制(图形验证码或者短信验证码、邮件验证码)

在用户提交数据之前,让用户输入验证码,或者用户在进行关键操作时,让用户重新输入密码进行验证。

4.校验非标准Http头部X-Requested-With

XMLHttpRequest,我们可以在服务端检查这个非标准Http头部X-Requested-With:XMLHttpRequest是否存在,如不存在,则可以认为是不安全的请求,拒绝提供相关服务。

注意:

如果网站同时还存在XSS漏洞或者开启CORS支持时,上述方法将可能失效,因为XSS可以模拟浏览器执行操作,构造出合法的AJAX用户请求,从而绕过非标准Http头部X-Requested-With:XMLHttpRequest校验,所以在做好CSRF防护的同时,相应的安全防护也应做好。

5.设置自定义的Http头部字段,比如CSRF:Token

我们可以在服务端检查这个自定义的Http头部字段是否存在,如不存在,则可以认为是不安全的请求,拒绝提供相关服务。

注意:

如果网站同时开启CORS支持时,上述方法将可能失效,因为攻击者可以模拟浏览器执行操作,构造出合法的AJAX用户请求,从而绕过自定义的Http头部字段校验,所以在做好CSRF防护的同时,相应的安全防护也应做好。

参考代码:

// 生成并存储CSRF-TokenString csrfToken = UUID.randomUUID().toString();session.setAttribute("csrfToken", csrfToken);
// 在表单中添加CSRF-Token字段String form = "<form action='/update' method='post'>" + "<input type='hidden' name='csrfToken' value='" + csrfToken + "'>" + "<input type='text' name='username'>" + "<input type='password' name='password'>" + "<input type='submit' value='Submit'>" +              "</form>";
// 在服务器端处理请求时,检查CSRF-Token和Referer字段if (request.getMethod().equalsIgnoreCase("post")) { String csrfTokenReceived = request.getParameter("csrfToken"); String csrfTokenExpected = (String) session.getAttribute("csrfToken");    String referer = request.getHeader("Referer");
// 检查CSRF-Token是否有效 if (csrfTokenReceived == null || !csrfTokenReceived.equals(csrfTokenExpected)) { // CSRF-Token无效,不处理请求 return; } // 检查Referer字段是否有效 if (referer == null || !referer.startsWith(request.getScheme() + "://" + request.getServerName())) { // Referer字段无效,不处理请求 return;    } // CSRF-Token和Referer字段都有效,处理请求 ...}

在这段代码中,我们首先生成了一个CSRF-Token并将其存储到了会话中。然后在表单中添加了一个隐藏字段来保存这个CSRF-Token。

在服务器端处理请求时,我们首先检查请求是否为POST请求,然后检查请求中的CSRF-Token字段和Referer字段。如果CSRF-Token无效或Referer字段无效,那么就不处理请求。否则,就处理请求。

测试过程:

数据采用GET或POST请求,数据包中各参数均为用户可控参数,没有设置CSRF-Token,没有验证Referer字段,没有图形验证码和短信、邮件验证码,同时也没有校验非标准Http头部X-Requested-With: XMLHttpRequest,没有设置自定义的Http头部字段,所以综上判断网站存在CSRF漏洞。

构造CSRF利用代码:

GET请求:

<html>  <body>    <form action="https://www.baidu.com/usma/api/v1/system/list?systemName=&amp;pageSize=100&amp;page=1" method="GET" name="form1">      <input type="hidden" name="systemName" value="" />      <input type="hidden" name="pageSize" value="100" />      <input type="hidden" name="page" value="1" />      <input type="submit" value="Submit request" />    </form>    <script>      history.pushState('', '', '/');</script>  </body></html>

文章来源: http://mp.weixin.qq.com/s?__biz=MzU1Mjk3MDY1OA==&mid=2247503243&idx=2&sn=ae980e58f7bd61ee7c802a9943cf40b7&chksm=fbfb7d46cc8cf450f8061b83e502a8326615a0809acc614b4cc51840a2597d94c75906ca3142#rd
如有侵权请联系:admin#unsafe.sh