什么是 CSRF
CSRF(Cross-Site Request Forgery)的全称是“跨站请求伪造”,也被称为“One Click Attack”或者“Session Riding”,通常缩写为CSRF或者XSRF。
攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
CSRF攻击其实是利用了web中用户身份认证验证的一个漏洞:简单的身份验证仅仅能保证请求发自某个用户的浏览器,却不能保证请求本身是用户自愿发出的。
CSRF攻击的原理及流程
几种常见的CSRF攻击类型
1、GET类型
仅仅须要一个HTTP请求,就能够构造一次简单的CSRF:
<img src=http://www.mybank.com/transfer?toBankId=11&money=1000>
当访问带有此图片的页面时,浏览器会自动发出一次这样的HTTP请求:http://www.mybank.com/transfer?toBankId=11&money=1000
。
2、POST类型
这种类型的CSRF攻击通常是利用一个自动提交的表单:
<form action="http://www.mybank.com/transfer" method=POST>
<input type="hidden" name="toBankId" value="11" />
<input type="hidden" name="money" value="10000" />
</form>
<script> document.forms[0].submit(); </script>
访问该页面时,表单会自动提交,相当于模拟用户完成了一次POST操作。
3、链接类型
这种类型的CSRF攻击不同于上面的两种,上面两种CSRF攻击只需要受害者进入页面就会触发,链接类型的CSRF攻击需要受害者点击链接才会触发:
<a href="http://www.mybank.com/transfer?toBankId=11&money=1000" taget="_blank">
看片点我
<a/>
CSRF攻击的特点
CSRF通常是跨域的,因为外域通常更容易被攻击者掌控。但是如果本域下有容易被利用的功能,比如可以发图和链接的论坛和评论区,攻击可以直接在本域下进行。
攻击利用受害者在被攻击网站的登录凭证,冒充受害者提交操作,而不是直接窃取数据(整个过程攻击者并不能获取到受害者的登录凭证,仅仅是“冒用”)。
防御策略
针对CSRF攻击的特点,采取措施:
特点一: CSRF通常是跨域的
措施:同源检测
特点二: CSRF攻击者不能获取到Cookie等信息,只是“冒用”
措施:CSRF Token
同源监测
既然CSRF通常是跨域的,那么就直接禁止外域(或者不受信任的域名)对我们发起的请求。
HTTP协议中,每个异步请求的Header都会有一个Referer属性:
Referer属性默认包含path和query:
Referer: http://www.mybank.com/transfer?toBankId=11&money=1000
Referer属性记录了该HTTP请求的来源地址。 对于Ajax请求,图片和script等资源请求,Referer为发起请求的页面地址。对于页面跳转,Referer为打开页面历史记录的前一个页面地址。因此我们可以使用Referer确定来源域名。
可以通过设置Referrer Policy来控制哪些访问来源信息可以在Referer中发送。(Referrer-Policy)
这种方法并非万无一失,Referer的值是由浏览器提供的,虽然HTTP协议上有明确的要求,但是每个浏览器对于Referer的具体实现可能有差别,并不能保证浏览器自身没有安全漏洞。使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不是很安全。在部分情况下,攻击者可以隐藏,甚至修改自己请求的Referer(Referer隐藏和伪造)。
CSRF大多数情况下来自第三方域名,但并不能排除本域发起。如果攻击者有权限在本域发布评论(含链接、图片等),那么它可以直接在本域发起攻击,这种情况下同源策略无法达到防御的作用。
补充:
除了Referer属性,Header中还有一个属性Origin,记录HTTP请求的来源地址,Origin属性不包含path和query:Origin: http://www.mybank.com
。但是Get和Head请求中没有Origin属性。
CSRF Token
CSRF攻击者不能获取到Cookie等信息,只是“冒用”。所以我们可以要求浏览器发送的请求中提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再进行CSRF攻击了。
当用户登陆的时候,生成一个随机数,作为csrfToken存放在Cookie中:
HttpServletResponse.addCookie(new Cookie("csrfToken", UUIDHelper.getUuid()));
实现校验的两个实现方式:
一、当发送请求时,将Cookie中的csrfToken取出作为一个参数拼接在请求后面,后端接收到请求后,将Cookie中的csrfToken和请求中的参数做对比,如果相同则允许访问,反正则不允许。
二、当发送请求时,将Cookie中的csrfToken取出放到请求Head中,后端接收到请求后,将Cookie中的csrfToken和请求Head中的csrfToken做对比,如果相同则允许访问,反正则不允许。
为什么可以把csrfToken放在Cookie中?
CSRF攻击者不能获取到Cookie信息,只要系统没有XSS漏洞或者其他泄露Cookie的漏洞,那么CSRF攻击者就无法获取到Cookie中的csrfToken。