深度聊聊CSRF攻击
前言
公司有项目需要入国网供电公司的内网,需要经过电科院的检测。虽然项目后端是Java,暂时不太熟,但是想详细聊聊CSRF攻击,为什么攻击者能拿到被攻击网站的cookie,以及Django项目该如何防范
仅是抛砖引玉,希望能引发大家的思考
1. csrf攻击的定义
CSRF 攻击指的是跨站请求伪造,攻击者诱导用户进入一个第三方网站,然后该网站向被攻击网站发送跨站请求。如果用户在被攻击网站中保存了登录状态,那么攻击者就可以利用这个登录状态,绕过后台的用户验证,冒充用户向服务器执行一些操作。
CSRF 攻击的本质是利用 cookie 会在同源请求中携带发送给服务器的特点,以此来实现用户的冒充。
2. 为什么攻击者的网站能拿到被攻击网站的cookie
-
原因一: cookie 会在请求时自动被浏览器携带,其他网站发起请求时仍然可以自动带上cookie
-
原因二:cookie不能跨域访问(除非是在cookie的domain允许的子域),但是可以跨站点共享。此时的被自动携带的 cookie 被称为 第三方cookie
a. 什么是跨域和跨站
详细可参考讲清楚同源、跨源、同站、跨站
站(site) 部分域名+顶级域名
在同一浏览器,当前打开的多个Tab页网站,无论是否为同一站点,cookie都是共享可见的。这个共享不是说每个网站的脚本可以访问别的网站的cookie,而是说,你向同一服务器发送请求时,会带上浏览器保存的对于那个服务器的所有cookie,而不管你从哪个网站发起的请求。
参考它
juejin.cn/post/695841…
b. cookie
什么是cookie
MDN给出的定义是服务器发送到用户浏览器并保存在本地的一小块数据。浏览器会存储 cookie 并在下次向同一服务器再发起请求时携带并发送到服务器上
服务器收到 HTTP 请求后,服务器可以在响应标头里面添加一个或多个 Set-Cookie
选项。浏览器收到响应后通常会保存下 Cookie,并将其放在 HTTP Cookie
标头内,向同一服务器发出请求时一起发送。你可以指定一个过期日期或者时间段之后,不能发送 cookie。你也可以对指定的域和路径设置额外的限制,以限制 cookie 发送的位置。关于下面提到的头部属性的详细信息,请参考 Set-Cookie
文章
这是因为HTTP协议是无状态的,也就是说,服务器无法知道请求是由哪个客户端发出的。为了解决这个问题,引入了cookie机制。当客户端第一次向服务器发送请求时,服务器会在响应头中添加一个Set-Cookie字段,该字段包含了一个唯一的标识符(cookie),并将该标识符存储在客户端的浏览器中。当客户端再次向服务器发送请求时,浏览器会自动将该标识符携带到服务器端,从而实现了状态的保持。
从定义里可知道 cookie 会在请求时自动被浏览器携带
3.典型场景
4. django前后端分离项目,如何防范 CSRF攻击
最常用的方法:使用csrf token。
设置CSRFtoken
官网如何做的 docs.djangoproject.com/zh-hans/4.1…
- a. 前端设置
前端获取cookie中的csrftoken,将其加入到请求头,字段名为X-CSRFToken
headers: {'X-CSRFToken': $.cookie('csrftoken')}
- b. 后端设置
手动调用 csrf 中的 get_token(request)
或 rotate_token(request)
方法
from django.middleware.csrf import get_token ,rotate_token
def server(request):
# get_token(request) // 两者选一
# rotate_token(request) // 此方法每次设置新的cookies
使用装饰器设置
from django.views.decorators.csrf import csrf_exempt,csrf_protect
csrf_protect是强制验证,在 csrf middleware未被注册时,依然可以强制csrf校验
#csrf_protect 可以使用默认的3种加装装饰器的方法
#csrf_exempt 只可以给dispatch加
参考 csrf原理以及前后端分离如何写入csrf_token blog.csdn.net/qq_41000891…
设置cookie的samesite属性
参考 zh.javascript.info/cookie#shu-…)
Cookie 的 samesite
选项提供了另一种防止此类攻击的方式,(理论上)不需要要求 “XSRF 保护 token”。
它有两个可能的值:
samesite=strict
(和没有值的samesite
一样)
如果用户来自同一网站之外,那么设置了 samesite=strict
的 cookie 永远不会被发送。
换句话说,无论用户是通过邮件链接还是从 evil.com
提交表单,或者进行了任何来自其他域下的操作,cookie 都不会被发送。
如果身份验证 cookie 具有 samesite
选项,那么 XSRF 攻击是没有机会成功的,因为来自 evil.com
的提交没有 cookie。因此,bank.com
将无法识别用户,也就不会继续进行付款。
这种保护是相当可靠的。只有来自 bank.com
的操作才会发送 samesite
cookie,例如来自 bank.com
的另一页面的表单提交。
虽然,这样有一些不方便。
当用户通过合法的链接访问 bank.com
时,例如从他们自己的笔记,他们会感到惊讶,bank.com
无法识别他们的身份。实际上,在这种情况下不会发送 samesite=strict
cookie。
我们可以通过使用两个 cookie 来解决这个问题:一个 cookie 用于“一般识别”,仅用于说 “Hello, John”,另一个带有 samesite=strict
的 cookie 用于进行数据更改的操作。这样,从网站外部来的用户会看到欢迎信息,但是支付操作必须是从银行网站启动的,这样第二个 cookie 才能被发送。
samesite=lax
一种更轻松的方法,该方法还可以防止 XSRF 攻击,并且不会破坏用户体验。
宽松(lax)模式,和 strict
模式类似,当从外部来到网站,则禁止浏览器发送 cookie,但是增加了一个例外。
如果以下两个条件均成立,则会发送含 samesite=lax
的 cookie:
HTTP 方法是“安全的”(例如 GET 方法,而不是 POST)。
所有安全的 HTTP 方法详见 RFC7231 规范。基本上,这些都是用于读取而不是写入数据的方法。它们不得执行任何更改数据的操作。跟随链接始终是 GET,是安全的方法。
该操作执行顶级导航(更改浏览器地址栏中的 URL)。
这通常是成立的,但是如果导航是在一个 中执行的,那么它就不是顶级的。此外,用于网络请求的 JavaScript 方法不会执行任何导航,因此它们不适合。
所以,samesite=lax
所做的是基本上允许最常见的“前往 URL”操作携带 cookie。例如,从笔记中打开网站链接就满足这些条件。
但是,任何更复杂的事儿,例如来自另一个网站的网络请求或表单提交都会丢失 cookie。
如果这种情况适合你,那么添加 samesite=lax
将不会破坏用户体验并且可以增加保护。
总体而言,samesite
是一个很好的选项。
但它有个缺点:
samesite
会被到 2017 年左右的旧版本浏览器忽略(不兼容)。
因此,如果我们仅依靠 samesite
提供保护,那么在旧版本的浏览器上将很容易受到攻击。
但是,我们肯定可以将 samesite
与其他保护措施一起使用,例如 XSRF token,这样可以多增加一层保护,将来,当旧版本的浏览器淘汰时,我们可能就可以删除 xsrf token 这种方式了。
5. 误区 误以为攻击者可以读取cookie
初步了解CSRF会发现,攻击者可以获取cookie,便认为从cookie里取token放在header里是脱裤子放屁,多此一举, 其实攻击者根本无法读取
cookie里面的详细信息,只能让浏览器自动携带请求而已。
所以我们最后直接比对cookie中的csrf token 和header里的csrf token即可知道有没有遭受攻击
为什么攻击者无法读取cookie,原因是浏览器的同源策略
6. 后端开发必踩的坑,关于自定义响应头的问题
自定义响应头,默认前端无法获取
return DetailResponse(data=ser.data, msg="获取成功",headers={
'token': 'no-st4444555','Access-Control-Expose-Headers':'token'})
Access-Control-Expose-Headers
指定允许访问的自定义头信息。