CSRF攻击和防范

52 阅读4分钟

背景

假如我在银行网站bank.com登录了,现在我打开attack.com浏览网页。网页中有意引导我点击某个按钮,然后我中招了。点击按钮后触发了bank.com转账请求,而我浑然不知。CSRF攻击就此成功。
请求能够成功的原因是浏览器的Cookie机制,当请求某个域名时,浏览器会自动携带其相关的Cookie。虽然attack.com没有我的Cookie,但是bank.com却拥有。
image

这张图不是我制作的,是之前看视频截下来的。

延伸

看到以上场景,很容易联想到。attack来发送bank的请求,这一步为什么会成功?一般来说这种需要引导攻击的请求,肯定是具有一定重要性的,那么直接不允许跨域不就行了。

答案如下:
表单提交的 Content-Type 通常是 application/x-www-form-urlencoded),浏览器就不会执行 CORS 预检(Preflight)。服务器也 不需要 发送任何特殊的 Access-Control-Allow-Origin 头部来允许它。

那么既然如此,其实防范CSRF攻击只要考虑表单就行了,其他的都不用考虑。

表单法

我看到这个方法是在Python的Django框架

在表单中添加一个额外的input元素,type='hidden',value为服务端生成。

<form>
    {% csrf_token %}
</form>

这样一来,提交表单中会有csrf_token字段,服务端只需要验证一下是否和生成的值相等,就可以避免攻击。

要点:
无法获取到csrf_token,因为它是服务端临时生成的。

疑问

假如攻击者用它自己的主机去模拟访问目标网站,从html响应中解析到value的值,再把这个值塞到自己的网站中,这样不就能够以假乱真吗?
答案如下:
表单中生成的token,是存在cookie中的。和前文背景中提到的一样,由于攻击者无法获取到cookie的具体值,因此他无法捏造一个input放到form表单中。

Header法(没啥意义)

服务端生成csrf_token,并交由前端存储(比如说放到Cookie中),并要求在请求时携带X-CSRF-TOKEN请求头。
依然是和前面一样,由于攻击者无法获取到Cookie的具体数值,因此无法捏造X-CSRF-TOKEN请求头。

疑虑

因为先前是传统表单不受跨域限制所以才导致csrf攻击成功。而传统表单不支持自定义header,所以这个header法完全没有意义,除非服务端允许任何站点进行跨域操作。

samesite cookie

兼容性相当好,从Chrome 51开始就支持了。在Chrome 80,cookie的默认SameSite值为Lax。
image.png

简单来说就是跨站点时可以不发送cookie,在浏览器层面解决了问题。

Set-Cookie: CookieName=CookieValue; SameSite=Strict;
Strict: 不允许任何跨站点请求携带Cookie
Lax: 允许GET请求方式的跨站点请求携带Cookie
None: 无限制

具体可以看看阮一峰写的:www.ruanyifeng.com/blog/2019/0…

解决Cookie或者表单

csrf攻击是浏览器自动携带Cookie的机制,以及表单无视跨域限制这两个条件下产生的结果。那么只要去掉其中一个条件,csrf攻击问题也就解决了。
客户端:
Cookie是浏览器的一个很好的功能,自带过期机制。如果浏览器网页因为csrf攻击而不去使用cookie,未免有些因噎废食。
但是,在客户端中是没有cookie这个东西的,因此也就不存在CSRF攻击的问题。

不使用表单:
只要不在网页中使用任何<form>表单,那么就不需要考虑csrf攻击问题。

题外话

httponly

设置为httponly的cookie不可被js代码直接获取。

从而防止了xss注入导致cookie泄露的情况。
xss注入是一种类似于sql注入一样的攻击手段。其凭借各种用户输入以注入,假如目标网站不对其进行处理,而是直接将用户输入插入到网站上(比如innerHTML=xxx),就会导致用户输入的script脚本执行,从而引发危险。