龙空技术网

一文深入了解CSRF漏洞2

欣于所遇所之既倦 503

前言:

现时小伙伴们对“restful和ajax的区别”可能比较注重,兄弟们都需要学习一些“restful和ajax的区别”的相关资讯。那么小编同时在网上网罗了一些对于“restful和ajax的区别””的相关内容,希望同学们能喜欢,姐妹们快快来学习一下吧!

简介: 1.1. 定义 跨站请求伪造 (英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding ,通常缩写为 CSRF 或者 XSRF , 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟 跨站脚本(XSS) 相比, XSS 利用的是用户对指定网站

1.1. 定义

跨站请求伪造(英语:Cross-site request forgery),也被称为 one-click attack 或者 session riding,通常缩写为 CSRF 或者 XSRF, 是一种挟制用户在当前已登录的Web应用程序上执行非本意的操作的攻击方法。跟跨站脚本(XSS)相比,XSS 利用的是用户对指定网站的信任,CSRF 利用的是网站对用户网页浏览器的信任。

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

Note

简单来说就是你点击我构造的恶意链接,我就可以以你的名义去发起一个http请求

1.2. 举例假如X银行用以执行转账操作的URL地址如下https://bank.example.com/withdraw?amount=1000&to=PayeeName一个恶意攻击者在另一个网站中中放置如下代码<img src="; />如果有登陆了X银行的用户访问恶意站点,那么就会携带cookie去请求对应的转账URL,向Bob转账1000元

Note

这种恶意的网址可以有很多种形式,藏身于网页中的许多地方,只要能让受害者发起对应的请求即可,如上述中的转账请求。

攻击者也不需要控制放置恶意代码的网站,例如他可以将这种地址藏在各大论坛,博客等任何用户生成内容的网站中,这意味着如果服务端没有合适的防御措施的话,用户即使访问熟悉的可信网站也有受攻击的危险

通过例子也能够看出,攻击者并不能通过CSRF攻击来直接获取用户的账户控制权,也不能直接窃取用户的任何信息。他们能做到的,是欺骗用户的浏览器,让其以用户的名义执行操作

1.3. 攻击流程

具体的攻击流程如下:

用户正常登录web服务,并一直保持在线服务器返回用户凭证Session ,并将其保存在Cookie中攻击者生成payload,并放置在用户可访问的地方攻击者诱导用户点击在第3步放置的链接,此时用户一直在线,且是用同一浏览器打开(保证Cookie未失效)用户点击恶意链接恶意链接向服务器请求,由于用户Cookie未失效,就携带用户Cookie访问服务器服务器收到请求,此时用户Cookie 未失效,并判定为“用户”发起的正常请求,并做出响应1.4. 分类1.4.1. GET型

这种是最容易利用的,相比于POST型来说,攻击面也大很多,比如上述CSRF转账例子中就是GET型的

在web应用中,很多接口通过GET进行数据的请求和存储,如果未对来源进行校验,并且没有token保护,攻击者可以直接通过发送含有payload的链接进行诱导点击;亦可以通过评论区或类似功能处发布图片,通过修改img地址的方式保存至页面,用户访问便会进行自动加载造成攻击

<!-- 不论什么手段,只要能让受害者访问一个链接即可 --><img src="; />
1.4.2. POST-表单型

相比于GET型,这种就要多很多,因为很多开发在提交数据的功能点时都会采用POST,如创建用户、创建文章、发消息等,利用起来也相对麻烦点

Note

测试时,为了扩大危害,可以尝试将POST数据包转换成GET数据包,后端采用如@RequestMaping("/")这种同时接受POST和GET请求的话,就可以成功

利用起来无非也是构造一个自动提交的表单,然后嵌入到页面中,诱导受害者访问,受害者访问后会自动提交表单发起请求

<form action= method=POST><input type="text" name="amount" value="1000" /></form><script> document.forms[0].submit(); </script>
1.4.3. POST-JSON型

现在越来越多的系统都采用RESTful风格开发,前后端分离,ajax请求后端获取数据再到前端渲染,所以上述表单型也越来越少了

如果我们发现请求头中的Content-Type值是application/json,基本上就可以确定采用了前后端分离了

这种一般有4⃣️种利用手法:

json转param闭合JSONajax发起请求flash+307跳转json转param

部分网站可能同时支持json和表单格式,所以我们可以尝试进行转换,也算是一个小tips吧

如把 {"a":"b"} 转换为 a=b,服务端可能也会解析

闭合JSON

这种要求对Content-Type没有限制,比如传输的数据为 {"a":"b"},那么我们就可以构造一个表单

<form action= method=POST>    <!-- 重点是下面这一行 -->    <input type="hidden" name='{"a":"' value='b"}' /></form><script> document.forms[0].submit(); </script>

这样自动提交表单的时候,提交的data就是 {"a":"=b"},闭合成了json

Note

实际环境中本人没遇到过,基本上遇到的都是强制要求Content-Type为json

ajax发起请求XMLHttpRequest跨域预检

当跨域影响用户数据HTTP请求(如用XMLHttpRequest发送get/post)时,浏览器会发送预检请求(OPTIONS请求)给服务端征求支持的请求方法,然后根据服务端响应允许才发送真正的请求。

HTTP/1.1 200 OKServer: Apache-Coyote/1.1Access-Control-Allow-Origin: : trueAccess-Control-Max-Age: 1800Access-Control-Allow-Methods: POSTAccess-Control-Allow-Headers: content-type,access-control-request-headers,access-control-request-method,accept,origin,x-requested-withContent-Length: 0Date: Wed, 11 Mar 2015 05:16:31 GMT

然而如果服务端对Content-Type进行校验,则不会响应这个OPTIONS请求,从而利用失败。但是更多的情况下服务端可能不会校验Content-Type,或者不会严格校验Content-Type是否为application/json,所以很多情况下这是可用的

<script>  windows.onload = () => {    var xhr = new XMLHttpRequest()    xhr.open("POST", ";)    xhr.setRequestHeader("Accept", "*/*")    xhr.setRequestHeader("Accept-Language", "zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3")    xhr.setRequestHeader("Content-Type", "application/json; charset=utf-8")    xhr.withCredentials = true // 携带cookie    xhr.send(JSON.stringify({"a":"b"})  }</script>
flash+307跳转

利用Flash的跨域与307跳转来绕过http自定义头限制,307跟其他3XX HTTP状态码之间的区别就在于,HTTP 307可以确保重定向请求发送之后,请求方法和请求主体不会发生任何改变。HTTP 307会将POST body和HTTP头重定向到我们所指定的最终URL,并完成攻击

详情参考该系列我的另一片文章:一次XSS和CSRF的组合拳进攻(CSRF+JSON)

1.5. 挖掘

算是一些挖掘经验吧,很多小伙伴都知道这个漏洞,但是不知道如何挖掘。

1.5.1. 应用场景

其实所有需要登陆认证且存在操作的地方,都可能存在CSRF;比如修改个人信息、发送邮件、创建管理员用户等等,只能查看的功能不考虑,因为不能算真正利用

1.5.2. 如何快速验证

Tip

观察数据包,如果header头和data中都没有token,然后尝试删除referer,还是能成功发送请求的话,就可以确定存在CSRF漏洞了

为了保险起见,在时间充足的情况下,还是需要尽量通过POC验证下,一般不需要2个账号进行验证,一个账号即可(2个只能说更保险)

非json的情况下,使用burp可以快速生成POC,也可以自己写,反正原理都是发起请求即可

登陆账号的情况下去访问这个poc,如果能成功得到自己的结果,就是OK的。

1.6. 防御

WEB的身份验证机制可以保证一个请求是来自于哪个用户的浏览器,但是却不能保证请求是否由本人发起的,所以修复和防御也是保证请求由用户本人发起即可。

Tip

简单来说,或者和客户沟通的情况下,直接说修复方法就是防止请求重放,他们开发也差不多都知道怎么修了

1.6.1. 令牌同步模式

令牌同步模式(英语:Synchronizer token pattern,简称STP)。

原理是:当用户发送请求时,服务器端应用将令牌(token:一个保密且唯一的值)嵌入HTML表格,并发送给客户端。客户端提交HTML表格时候,会将令牌发送到服务端,再由服务端对令牌进行验证。令牌可以通过任何方式生成,只要确保随机性和唯一性。这样确保攻击者发送请求时候,由于没有该令牌而无法通过验证。(没有token不能重放数据包)

<input type="hidden" name="_csrf_token" value="YidlXHhlMVx4YmJceDkxQFx4OTdceDg5a1x4OTJcbic=">

Note

STP能在HTML下运作顺利,但会导致服务端的复杂度升高,复杂度源于令牌的生成和验证。因为令牌是唯一且随机,如果每个表格都使用一个唯一的令牌,那么当页面过多时,服务器由于生产令牌而导致的负担也会增加。而使用会话(session)等级的令牌代替的话,服务器的负担将没有那么重。

1.6.2. 检查Referer字段

HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下

以上文银行操作为例,Referer字段地址通常应该是转账按钮所在的网页地址,应该也位于bank.example.com之下。而如果是CSRF攻击传来的请求,Referer字段会是包含恶意网址的地址,不会位于bank.example.com之下,这时候服务器就能识别出恶意的访问。

Warning

这种办法简单易行,工作量低,仅需要在关键访问处增加一步校验。

但这种办法也有其局限性,因其完全依赖浏览器发送正确的Referer字段;虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段,并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。

1.6.3. 添加校验token

Note

提交不一定是在data里面提交,也可以在header里面

由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再执行CSRF攻击。

这种数据通常是窗体中的一个数据项。服务器将其生成并附加在窗体中,其内容是一个伪随机数。当客户端通过窗体提交请求时,这个伪随机数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪随机数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪随机数的值,服务端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

1.6.4. 一次一用验证码

在关键操作处添加一次一用的验证码,攻击者无法事先知道验证码的值,也就无法成功构造发起请求的数据包。

Attention

需要用户交互,如果很多地方都加上,用户体验极差,所以一般不建议这个

1.6.5. 使用SameSite Cookie

设置SameSite属性,需要根据需要设置

如果Samesite Cookie被设置为Strict,浏览器在任何跨域请求中都不会携带Cookie,新标签重新打开也不携带,所以说CSRF攻击基本没有机会;但是跳转子域名或者是新标签重新打开刚登陆的网站,之前的Cookie都不会存在。尤其是有登录的网站,那么我们新打开一个标签进入,或者跳转到子域名的网站,都需要重新登录。对于用户来讲,可能体验不会很好。如果Samesite Cookie被设置为Lax,那么其他网站通过页面跳转过来的时候可以使用Cookie,可以保障外域连接打开页面时用户的登录状态。但相应的,其安全性也比较低。1.7. 个人预防

网站如果存在CSRF漏洞,个人一般要如何操作才能防止攻击到自己呢?

尽量每次使用隐私浏览器,因为其关闭后会清空所有的cookie不要随便打开链接,一定要打开的情况下,可以使用隐私浏览器

附录:

后端防御CSRF

我们先聊聊后端的防御,后端防御主要是区分哪些请求是恶意请求,哪些请求是自己网站的请求。区分恶意请求的方式有很多,在这里给大家介绍两种吧。

第一种,CSRF Token的方式。这种方式是在表单页面生成一个随机数,这个随机数一定要后端生成,并且对这个随机数进行存储。在前端页面中,对这个Token表单项进行隐藏。代码如下:

<form method="post" action="/transfer">    <input type="hidden" name="_csrf" value="4bfd1575-3ad1-4d21-96c7-4ef2d9f86721"/>    <input type="text" name="amount"/>    <input type="hidden" name="account"/>    <input type="submit" value="Transfer"/></form>

_csrf就是CSRF Token。我们看到他的value是一个UUID,这个UUID是后台生成的。当用户点击转账按钮时,会给银行的后台发送请求,请求中包含_csrf参数,如下:

POST /transfer HTTP/1.1Host: : JSESSIONID=randomidContent-Type: application/x-www-form-urlencodedamount=100.00&account=9876&_csrf=4bfd1575-3ad1-4d21-96c7-4ef2d9f86721

银行后台接收到这个请求后,判断_csrf的值是否存在,如果存在则是自己网站的请求,进行后续的流程;如果不存在,则是恶意网站的请求,直接忽略。

第二种,通过请求头中的referer字段判断请求的来源。每一个发送给后端的请求,在请求头中都会包含一个referer字段,这个字段标识着请求的来源。如果请求是从银行网站发出的,这个字段会是银行网站转账页的链接,比如:;如果是从恶意网站发出的,那么referer字段一定不会是银行网站。我们在做后端防御时,可以先取出每个请求的请求头中的referer字段,判断是不是以自己网站的域名开头,在咱们的示例中,如果referer字段是以开头的,则继续执行转账操作;如果不是,则直接忽略掉这个请求。

以上就是后端防御CSRF攻击的两种方式,都需要在后端做特殊的处理。当然也可以在前端做处理,怎么做呢?我们接着往下看。

前端防御CSRF

既然CSRF攻击的危害这么大,为什么不能在前端禁止这种请求呢?各大浏览器厂商似乎也注意到了这个问题,谷歌提出了same-site cookies概念,same-site cookies 是基于 Chrome 和 Mozilla 开发者花了三年多时间制定的 IETF 标准。它是在原有的Cookie中,新添加了一个SameSite属性,它标识着在非同源的请求中,是否可以带上Cookie,它可以设置为3个值,分别为:

StrictLaxNone

Cookie中的内容为:

POST /transfer HTTP/1.1Host: : JSESSIONID=randomid;SameSite=Strict;

Strict是最严格的,它完全禁止在跨站情况下,发送Cookie。只有在自己的网站内部发送请求,才会带上Cookie。不过这个规则过于严格,会影响用户的体验。比如在一个网站中有一个链接,这个链接连接到了GitHub上,由于SameSite设置为Strict,跳转到GitHub后,GitHub总是未登录状态。

Lax的规则稍稍放宽了些,大部分跨站的请求也不会带上Cookie,但是一些导航的Get请求会带上Cookie,如下:

请求类型

示例

Lax情况

链接

<a href="..."></a>

发送 Cookie

预加载

<link rel="prerender" href="..."/>

发送 Cookie

GET 表单

<form method="GET" action="...">

发送 Cookie

POST 表单

<form method="POST" action="...">

不发送

iframe

frameLabelStart--frameLabelEnd

不发送

AJAX

$.get("...")

不发送

Image

<img src="...">

不发送

上面的表格就是SameSite设置为Lax的时候,Cookie的发送情况。

None就是关闭SameSite属性,所有的情况下都发送Cookie。不过SameSite设置None,还要同时设置Cookie的Secure属性,否则是不生效的。

以上就是在前端通过Cookie的SameSite属性防御CSRF攻击,不过大家在使用SameSite属性时,要注意浏览器是否支持SameSite属性。

标签: #restful和ajax的区别