详细聊聊CSRF攻击,以及Django项目应该如何防范

2023年 9月 12日 87.3k 0

深度聊聊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.典型场景

  • 钓鱼网站:攻击者创建一个看似合法的网站,引诱用户访问。在该网站上,攻击者可能包含了针对另一个网站的恶意CSRF请求。当用户在不知情的情况下登录到目标网站时,他们的认证信息将用于执行恶意请求。
  • 恶意电子邮件或消息:攻击者可以发送包含恶意链接或图像的电子邮件或社交媒体消息。当用户查看这些电子邮件或消息时,可能会在后台执行CSRF攻击。
  • 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
     指定允许访问的自定义头信息。
    

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论