简介
CSRF(Cross-site request forgery)跨站请求伪造:攻击者诱导受害者进入第三方网站,在第三方网站中,向被攻击网站发送跨站请求。利用受害者在被攻击网站已经获取的注册凭证,绕过后台的用户验证,达到冒充用户对被攻击的网站执行某项操作的目的。
一个典型的CSRF攻击有着如下的流程:
- 受害者登录a.com,并保留了登录凭证(Cookie)。
- 攻击者引诱受害者访问了thirdsite.com。
- thirdsite.com 向 a.com 发送了一个请求:a.com/act=xx
- a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求。
- 即thirdsite.com此时拿到了 a.com给用户种下的cookie,所以thirdsite.com 请求的时候也带上了a.com网站的cookie,从而绕过了验证
- a.com以受害者的名义执行了act=xx。
- 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作。
thirdsite.com在发出请求的时候 可以 携带 a.com的网站的cookie?
===> thirdsite.com 发出的请求是 a.com/action=xx,所以浏览器侧看到请求是a.com就会自动带上a网站的cookie;
很明显 thirdsite.com域名下 发出 a.com 的请求,是发生了跨域行为的,一般浏览器会对跨域请求的返回进行拦截;但是发还是发的出去的啊!
所以一般用于,对用户数据进行服务器访问的更改请求,而拿不到服务器返回的数据
在这个攻击过程中,攻击者借助受害者的 Cookie 骗取服务器的信任,但并不能拿到 Cookie,也看不到 Cookie 的内容。
而对于服务器返回的结果,由于浏览器同源策略的限制,攻击者也无法进行解析。
(攻击者的网站虽然是跨域的,但是他构造的链接(/请求)是源网站的,跟源网站是同源的,所以能够携带cookie发起访问)。
但是攻击者无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。例如删除数据、修改数据,新增数据等,无法获取数据。
常见CSRF攻击
GET类型
GET类型的CSRF利用非常简单,只需要一个HTTP请求,一般会这样利用:
<img src="http://bank.example/withdraw?amount=10000&for=hacker" >
在受害者访问含有这个img的页面后,浏览器会自动向http://bank.example/withdraw?account=xiaoming&amount=10000&for=hacker发出一次HTTP请求。bank.example就会收到包含受害者登录信息的**一次跨域请求**。
POST类型
这种类型的CSRF利用起来通常使用的是一个自动提交的表单,如:
<form action="http://bank.example/withdraw" method=POST><input type="hidden" name="account" value="xiaoming" /><input type="hidden" name="amount" value="10000" /><input type="hidden" name="for" value="hacker" /></form><script> document.forms[0].submit(); </script>
链接类型
诱骗点击,点击的一瞬间发出get请求
<a href="http://test.com/csrf/withdraw.php?amount=1000&for=hacker" taget="_blank">重磅消息!!<a/>
防范
CSRF的两个特点:
- CSRF(通常)发生在第三方域名。
- CSRF攻击者不能获取到Cookie等信息,只是使用。
针对这两点,我们可以专门制定防护策略,如下:
- 阻止不明外域的访问
- 同源检测
- Samesite Cookie
- 提交时要求附加本域才能获取的信息
- CSRF Token
- 双重Cookie验证
同源策略检测
既然CSRF大多来自第三方网站,那么我们就直接禁止外域(或者不受信任的域名)对我们发起请求
如何判断请求是否来自外域呢?
在HTTP协议中,每一个异步请求都会携带两个Header,用于标记来源域名:
- Origin Header
- Referer Header
但是Origin在以下两种情况下并不存在: IE 11 不会在跨站CORS请求上添加Origin标头;302重定向;
所以一般用referer
使用验证 Referer 值的方法,就是把安全性都依赖于第三方(即浏览器)来保障,从理论上来讲,这样并不是很安全。在部分情况下,攻击者可以隐藏,甚至修改自己请求的Referer。
Token
CSRF的另一个特征是,攻击者无法直接窃取到用户的信息(Cookie,Header,网站内容等),仅仅是冒用Cookie中的信息
而CSRF攻击之所以能够成功,是因为服务器误把攻击者发送的请求当成了用户自己的请求。那么我们可以要求所有的用户请求都携带一个CSRF攻击者无法获取到的Token。
(当然是前端工程师手动写的啦 而不是依靠浏览器自动带上;手动将给请求的header注入token)
服务器通过校验请求是否携带正确的Token,来把正常的请求和攻击的请求区分开,也可以防范CSRF的攻击
cookie改良
samesite属性
Set-Cookie响应头新增Samesite属性,它用来标明这个 Cookie是个“同站 Cookie”,同站Cookie只能作为第一方Cookie,不能作为第三方Cookie
Samesite=Strict
表明这个 Cookie 在任何情况下都不可能作为第三方 Cookie,绝无例外。比如说 b.com 设置了如下 Cookie:
lax:假如这个请求是这种请求(改变了当前页面或者打开了新页面)且同时是个GET请求,则这个Cookie可以作为第三方Cookie;异步请求就不行 or post的表单提交也不行;
# 在 a.com 下发起对 b.com 的任意请求,# foo 这个 Cookie 都不会被包含在 Cookie 请求头中Set-Cookie: foo=1; Samesite=Strict# bar这个cookie会包含在请求中Set-Cookie: bar=2; Samesite=LaxSet-Cookie: baz=3
验证码
验证码被认为是对抗 CSRF 攻击最简洁而有效的防御方法。从上述示例中可以看出,CSRF 攻击往往是在用户不知情的情况下构造了网络请求。
而验证码会强制用户必须与应用进行交互,才能完成最终请求。因为通常情况下,验证码能够很好地遏制 CSRF 攻击。但验证码并不是万能的,因为出于用户考虑,不能给网站所有的操作都加上验证码。因此,验证码只能作为防御 CSRF 的一种辅助手段,而不能作为最主要的解决方案。
