什么是csrf
csrf即跨域请求伪造,在用户未安全退出A站时,攻击者诱导用户点击第三方网站,第三方网站向A站发起请求,此时浏览器当作是用户发起的请求并加上了用户的凭证,而不是第三方请求,A站接受到请求后执行一系列操作,但是此时用户自身并不知情。
特点:
- 攻击发生在第三方而不是受攻击的站点,因此很难防御
- 在整个过程中,利用受害者在被攻击网站的登陆凭证,冒充受害者提交操作,而不是盗取凭证
- 整个过程中,攻击者并不能获取受害者的凭证,冒充受害者的身份
产生csrf的原因是什么
- 网站相信浏览器
- 当用户从第三方网站访问某个信任的站点时,浏览器直接使用已经保存的凭证
csrf攻击方式
-
Get型
csrf
这种类型比较简单,通常的用法是:
<img src=http://www.xxx.com/?amount=1000&for=hack>
当用户访问这个第三方页面时,就会自动向信任的站点发起请求
-
POST
型csrf
这种类型的利用方式通常为:
<form action="http://www.xxx.com/" method="POST"> <input type="hidden" name="amount" value="1000"> <input type="hidden" name="for" value="hack"> </form> <script> document.forms[0].submit(); </script>
当受害者点击包含上述的代码时就会自动向目标网站发起请求
-
链接型
这种方式不如上面的两种方式好用,上面的都是自动发送请求,但是这种必须要用户点击才可以<a href="http://www.xxx.com/?mount=1000&for=hack" target="_blank">...</a>
如何防御csrf漏洞
两个重要点:
1. 拒绝不安全的第三方域请求(同源检测 + samesite cookie)
- 使得第三方域不能发起正确请求(Token + 验证码 + 双重cookie)
阻止不明外域的访问
-
同源检测
在
HTTP
协议中,有两个字段用来标记发起请求的域的名称,origin
和referer
-
origin
一般在进行跨域请求的时候,浏览器会在请求头上加入
origin
字段,如果在请求头中有这个字段的话,就可以知道请求的来源域。但是origin
字段在一下两种情况下并不存在
IE 11
不会在跨域访问的时候加上origin
字段,referer
字段是唯一的判断标准302
重定向请求不会在请求头中出现origin
字段,因为302
重定向都是将URL
重定向到新的服务器上面去,而浏览器不想将origin
泄露到新的服务器上面去
-
referer
referer
字段记录了当前HTTP
请求的来源地址,referer
的值是由浏览器提供的,每个浏览器对referer
的具体实现是有差异的,所以如果让安全性完全依赖于浏览器的referer
值是不安全的,在有些情况下用户可以修改自己的referer
或者增加referer
字段2014年发布的
referer policy
规定了五种referer
策略
其中的same origin
表示同源的网站需要加上referer
,不同源的不加上设置
referer
策略有以下几种方式:
- 在
CSP
中进行设置 - 在
html
文档首的meta
中进行设置 - 在
a
标签中进行设置,属性名为referrerpolicy
以下几种情况中没有
referer
或者referer
不可信:IE 6 or 7
的window.location.href
和window.open
,会丢失referer
- 在所有浏览器中,
HTTPS
跳转到HTTP
时,referer
也会丢失 - 点击
Flash
到达某一个网站时,referer
很杂乱,不可信
一般情况下,如果
referer
和origin
都不存在时,直接对这个网站进行拦截。但是我们不能拦截掉所有的外域的请求,比如说使用百度等搜索引擎时,这种情况下,请求的头部一般是:Accept: text/html Method: Get
当然,允许这种请求的前提是,
GET
参数不会引起站点的数据的改变虽然说大部分情况下
csrf
是由第三方站点发起的,但是也有一些是在本域发起的,例如:黑客利用xss
漏洞,在本域发表评论等,这个时候仅仅依靠同源机制是不够的-
Samesite Cookie
Samesite Cookie
属性是set-Cookie
的新属性,这个属性标识了这个Cookie
是否能够被当作第三方站点的cookie
Samesite
属性有两个属性值,Strict
和Lax
-
Strict
Set-Cookie: foo=1; Samesite=Strict
,这种情况下,通过第三方站点访问本域的时候,不会加上foo
-
Lax
Set-Cookie: aoo=1; Samesite=Lax
,在这种情况下,通过第三方站点访问本域时,如果是GET
请求,那么久加上aoo
这个值,但是如果是通过POST
方法提交数据的话,这个cookie
值还是不会被添加进去
-
附加只有在本域才能获取的信息
这一点是利用了攻击者不能获取cookie
的特点
CSRF TOKEN
或者验证码
-
在用户第一次访问站点时,生成一个
Token
存放在session
中。 -
随后用户访问的每一个页面在加载时遍历整棵
DOM
树,在每个a
和form
标签后都加入Token
。 -
发起请求时,都会带上这个
Token
值GET http://xxx.com?csrf_token=... POST <input type="hidden" name="csrf_token"/>
-
服务端验证
Token
值是否正确,验证的时候要看token
是否正确以及时间时候过期这两点由于攻击者不知道
token
值,自然就不能伪造请求了,但是站点如果存在xss
漏洞的话,就可能导致攻击者盗取到token
,xss+csrf
有时候可以产生很大的危害。
-
双重
Cookie
采用
Token
的方式会占用大量的内存,双重Cookie
的方式会减少内存的占用。双重cookie
的校验流程如下:
- 前端提取出
cookie
中的token
值,并且把token
值加入到URL
中,使其变成:http://xxx.com?csrf_token=xxxx
- 后端校验
csrf_token
的值与cookie
中的值是否相同,如果相同则接受请求,否则拒绝请求
可以看到这种方式与Token
的方法相比简单很多,但是这种方法并没有得到大规模的应用,原因如下:
cookie
只在子域的话:如果用户访问的网站为www.a.com
,但是后端的网站为api.a.com
,那么前端页面是拿不到后端的cookie
的(时间顺序不一样),那么就无法完成双重cookie
验证cookie
在根域下的话:如果认证cookie
被放在a.com
下,每个子域都可访问和改变这个值,并且如果某一个子域页面存在漏洞被xss
攻击,那么就获取了这个父域的cookie
,导致所有的页面陷于危险,并且可以利用xss
漏洞自己配置cookie
值
参考链接:https://juejin.im/post/5bc009996fb9a05d0a055192#heading-30