跨站点请求伪造保护

CSRF中间件和模板标记提供了易于使用的保护 Cross Site Request Forgeries 。当恶意网站包含链接、表单按钮或一些旨在使用在其浏览器中访问恶意站点的登录用户的凭据在您的网站上执行某些操作的Java脚本时,就会发生此类攻击。文中还介绍了一种相关的攻击类型--“登录CSRF”,即攻击站点诱骗用户的浏览器使用其他用户的凭据登录到站点。

对CSRF攻击的第一个防御措施是确保GET请求(和其他安全的方法,如 RFC 9110#section-9.2.1 )是无副作用的。然后,通过POST、PUT和DELETE等不安全方法发出的请求可以通过中概述的步骤进行保护 如何使用Django的CSRF保护

它是如何工作的

CSRF保护基于以下内容:

  1. 作为随机保密值的CSRF Cookie,其他站点将无法访问。

    CsrfViewMiddleware 将此Cookie与响应一起发送到 django.middleware.csrf.get_token() 被称为。它也可以在其他情况下发送它。出于安全原因,每次用户登录时都会更改机密的值。

  2. 一个名为‘csrfmidleware Token’的隐藏表单域,出现在所有传出的POST表单中。

    为了保护自己免受 BREACH 攻击时,此字段的值不是简单的秘密。它使用掩码对每个响应进行不同的加扰。每次调用时都会随机生成掩码 get_token() ,因此每次的表单字段值都不同。

    此部分由模板标记完成。

  3. 对于所有不使用HTTP GET、HEAD、OPTIONS或TRACE的传入请求,必须存在CSRF cookie,并且“CSRFMiddleWareToken”字段必须存在且正确。否则,用户将收到403错误。

    验证“csrfmiddlewaretoken”字段值时,仅将机密(而不是完整令牌)与cookie值中的机密进行比较。这允许使用不断变化的令牌。虽然每个请求都可以使用自己的令牌,但机密仍然是所有人都有的。

    这张支票是由 CsrfViewMiddleware .

  4. CsrfViewMiddleware 验证 Origin header ,如果由浏览器提供,则针对当前主机和 CSRF_TRUSTED_ORIGINS 布景。这提供了针对跨子域攻击的保护。

  5. 此外,对于HTTPS请求,如果 Origin 未提供标头, CsrfViewMiddleware 执行严格的引用检查。这意味着即使子域可以在您的域上设置或修改Cookie,它也不能强制用户发布到您的应用程序,因为该请求不会来自您自己的确切域。

    这也解决了一个中间人在使用会话独立的秘密时,在HTTPS下可能受到的攻击,因为HTTP Set-Cookie 不幸的是,客户机甚至在使用HTTPS与站点通信时也会接受报头。(没有对HTTP请求执行引用检查,因为存在 Referer 头在HTTP下不够可靠。)

    如果 CSRF_COOKIE_DOMAIN 设置后,会将引用与其进行比较。您可以通过包含一个前导点来允许跨子域请求。例如, CSRF_COOKIE_DOMAIN = '.example.com' 将允许来自 www.example.comapi.example.com 。如果未设置该设置,则Referer必须与HTTP匹配 Host 标题。

    将接受的引用扩展到当前主机或cookie域之外,可以使用 CSRF_TRUSTED_ORIGINS 设置。

这确保只有源自受信任域的表单才能用于回发数据。

它故意忽略GET请求(以及其他被定义为安全的请求 RFC 9110#section-9.2.1 )。这些请求应该永远不会有任何潜在的危险副作用,因此带有GET请求的CSRF攻击应该是无害的。 RFC 9110#section-9.2.1 将POST、PUT和DELETE定义为“不安全”,所有其他方法也被假定为不安全,以获得最大限度的保护。

CSRF保护无法抵御中间人攻击,因此使用 HTTPS 具有 HTTP严格传输安全 . 它也假定 validation of the HOST header 而且没有 cross-site scripting vulnerabilities 在您的站点上(因为XSS漏洞已经让攻击者做任何CSRF漏洞允许的事情,而且更糟)。

去掉 Referer 页眉

为了避免向第三方网站公开引用URL,您可能需要 disable the referer 在你的网站上 <a> 标签。例如,您可以使用 <meta name="referrer" content="no-referrer"> 标记或包括 Referrer-Policy: no-referrer 标题。由于CSRF保护对HTTPS请求进行严格的引用检查,这些技术会导致使用“不安全”方法的请求出现CSRF故障。相反,使用诸如 <a rel="noreferrer" ...>" 用于链接到第三方网站。

局限性

站点内的子域将能够在客户端上为整个域设置Cookie。通过设置Cookie并使用相应的令牌,子域将能够绕过CSRF保护。避免这种情况的唯一方法是确保子域由受信任的用户控制(或者至少无法设置Cookie)。请注意,即使没有CSRF,也存在其他漏洞,例如会话固定,这使得将子域提供给不受信任方是一个坏主意,并且这些漏洞不容易使用当前的浏览器修复。

公用事业

下面的示例假设您使用的是基于函数的视图。如果您使用的是基于类的视图,则可以参考 Decorating class-based views .

csrf_exempt(view)[源代码]

这个修饰器将视图标记为不受中间件所保证的保护。例子::

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt


@csrf_exempt
def my_view(request):
    return HttpResponse("Hello world")
Changed in Django 5.0:

添加了对包装异步视图函数的支持。

csrf_protect(view)

提供保护的 CsrfViewMiddleware 一种观点。

用法:

from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect


@csrf_protect
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)
Changed in Django 5.0:

添加了对包装异步视图函数的支持。

requires_csrf_token(view)

通常 csrf_token 如果 CsrfViewMiddleware.process_view 或者类似的 csrf_protect 还没有运行。视图修饰符 requires_csrf_token 可用于确保模板标记正常工作。这个修饰符的工作原理与 csrf_protect ,但从不拒绝传入请求。

例子::

from django.shortcuts import render
from django.views.decorators.csrf import requires_csrf_token


@requires_csrf_token
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)
Changed in Django 5.0:

添加了对包装异步视图函数的支持。

此修饰符强制视图发送CSRF cookie。

Changed in Django 5.0:

添加了对包装异步视图函数的支持。

设置

许多设置可用于控制Django的CSRF行为:

常见问题

发布任意CSRF令牌对(cookie和post数据)是否是一个漏洞?

不,这是按设计的。如果没有中间人攻击,攻击者就无法将CSRF令牌cookie发送到受害者的浏览器,因此成功的攻击需要通过XSS或类似工具获取受害者的浏览器cookie,在这种情况下,攻击者通常不需要CSRF攻击。

一些安全审计工具将此标记为问题,但如前所述,攻击者无法窃取用户浏览器的CSRF cookie。”偷窃或修改 你自己 使用Firebug、chrome dev工具等的令牌不是一个漏洞。

Django的CSRF保护在默认情况下没有链接到会话是一个问题吗?

不,这是按设计的。不将CSRF保护链接到会话允许在诸如 纸屑箱 允许来自没有会话的匿名用户的提交。

如果要在用户会话中存储CSRF令牌,请使用 CSRF_USE_SESSIONS 设置。

为什么用户登录后会遇到CSRF验证失败?

出于安全原因,每次用户登录时都会旋转CSRF令牌。登录前生成表单的任何页面都将具有旧的、无效的CSRF令牌,需要重新加载。如果用户在登录后使用“后退”按钮或登录到其他浏览器选项卡,则可能会发生这种情况。