如何使用Django的CSRF保护

要在您的视图中利用CSRF保护,请执行以下步骤:

  1. 默认情况下,CSRF中间件在 MIDDLEWARE 布景。如果覆盖该设置,请记住 'django.middleware.csrf.CsrfViewMiddleware' 应该放在任何假定CSRF攻击已经得到处理的视图中间件之前。

    如果您禁用了它(不推荐),则可以使用 csrf_protect() 在您想要保护的特定视图上(见下文)。

  2. 在任何使用POST表单的模板中,使用 csrf_token 标记内的 <form> 元素,如果表单用于内部URL,例如:

    <form method="post">{% csrf_token %}
    

    不应对以外部URL为目标的POST表单执行此操作,因为这会导致CSRF令牌泄漏,从而导致漏洞。

  3. 在相应的视图函数中,确保 RequestContext 用于呈现响应,以便 {% csrf_token %} 将正常工作。如果您正在使用 render() 函数、通用视图或构造性应用程序,因为这些都使用了 RequestContext

通过AJAX使用CSRF保护

虽然上面的方法可以用于AJAX POST请求,但它有一些不便之处:您必须记住将CSRF令牌作为POST数据传递给每个POST请求。因此,还有一种替代方法:在每个XMLHttpRequest上,设置一个定制 X-CSRFToken 标头(由 CSRF_HEADER_NAME 设置)设置为CSRF令牌的值。这通常更容易,因为许多JavaScript框架提供了允许在每个请求上设置标头的挂钩。

首先,您必须获得CSRF令牌。如何做到这一点取决于 CSRF_USE_SESSIONSCSRF_COOKIE_HTTPONLY 设置已启用。

在AJAX请求上设置令牌

最后,您需要在您的AJAX请求上设置头。使用 fetch() 接口:

const request = new Request(
    /* URL */,
    {
        method: 'POST',
        headers: {'X-CSRFToken': csrftoken},
        mode: 'same-origin' // Do not send CSRF token to another domain.
    }
);
fetch(request).then(function(response) {
    // ...
});

在JJIA2模板中使用CSRF保护

姜戈的 Jinja2 模板后端添加 {{ csrf_input }} 添加到所有模板的上下文中,它等同于 {% csrf_token %} 用Django模板语言编写。例如:

<form method="post">{{ csrf_input }}

使用装饰符方法

而不是添加 CsrfViewMiddleware 作为全面保护,您可以使用 csrf_protect() 在需要保护的特定视图上,它具有完全相同的功能。它必须被使用 both 在输出中插入CSRF标记的视图,以及接受POST表单数据的视图。(这些通常是相同的查看功能,但并不总是)。

单独使用装饰器本身是 not recommended ,因为如果你忘记使用它,你会有一个安全漏洞。同时使用“腰带和支架”的策略很好,而且只会产生最少的开销。

处理被拒绝的请求

默认情况下,如果传入请求未通过由执行的检查,则会向用户发送‘403禁止’响应 CsrfViewMiddleware 。通常只有当存在真正的跨站点请求伪造时,或者由于编程错误,CSRF令牌未包含在POST表单中时,才会出现这种情况。

但是,错误页不是很友好,因此您可能希望提供您自己的视图来处理这种情况。为此,请将 CSRF_FAILURE_VIEW 布景。

CSRF故障将作为警告记录到 django.security.csrf 伐木者。

将CSRF保护与缓存配合使用

如果 csrf_token 模板标记由模板使用(或 get_token 函数以某种其他方式调用), CsrfViewMiddleware 将添加一个Cookie和一个 Vary: Cookie 响应的标头。这意味着如果按照指令使用中间件,中间件将与缓存中间件配合得很好 (UpdateCacheMiddleware 优先于所有其他中间件)。

然而,如果您在各个视图上使用缓存修饰符,CSRF中间件将还不能设置Variable标头或CSRF cookie,并且响应将被缓存,而没有这两者。在这种情况下,在任何需要插入CSRF令牌的视图上,您都应该使用 django.views.decorators.csrf.csrf_protect() 装饰者优先::

from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import csrf_protect


@cache_page(60 * 15)
@csrf_protect
def my_view(request):
    ...

如果您使用的是基于类的视图,可以参考 Decorating class-based views

测试和CSRF保护

这个 CsrfViewMiddleware 这通常是测试视图功能的一大障碍,因为需要CSRF令牌,它必须与每个POST请求一起发送。出于这个原因,Django的用于测试的HTTP客户端已经被修改为在请求上设置一个标志,从而放松中间件和 csrf_protect 这样他们就不再拒绝请求了。在所有其他方面(例如,发送Cookie等),它们的行为是相同的。

如果,出于某种原因,你 want 要执行CSRF检查的测试客户端,您可以创建强制执行CSRF检查的测试客户端的实例:

>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)

边缘情况

某些视图可能具有不寻常的要求,这意味着它们不符合这里设想的正常模式。在这些情况下,许多实用程序可能会很有用。下一节描述了可能需要它们的场景。

仅对少数几个视图禁用CSRF保护

大多数视图需要CSRF保护,但少数视图不需要。

解决方案:而不是禁用中间件并应用 csrf_protect 对于所有需要它的视图,启用中间件并使用 csrf_exempt()

在以下情况下设置令牌 CsrfViewMiddleware.process_view() 未使用

在某些情况下, CsrfViewMiddleware.process_view 在您的视图运行之前可能没有运行过--例如,404和500个处理程序--但是您仍然需要表单中的CSRF令牌。

解决方案:使用 requires_csrf_token()

在不受保护的视图中包含CSRF令牌

可能有一些不受保护的视图已被豁免 csrf_exempt ,但仍需要包括CSRF令牌。

解决方案:使用 csrf_exempt() 紧随其后的是 requires_csrf_token() 。(即 requires_csrf_token 应该是最里面的装饰者)。

仅保护一条路径的视图

一个视图只在一组条件下需要CSRF保护,并且不能在其余时间拥有它。

解决方案:使用 csrf_exempt() 对于整个视图函数,以及 csrf_protect() 因为它里面的路径需要保护。示例::

from django.views.decorators.csrf import csrf_exempt, csrf_protect


@csrf_exempt
def my_view(request):
    @csrf_protect
    def protected_path(request):
        do_something()

    if some_condition():
        return protected_path(request)
    else:
        do_something_else()

保护使用AJAX而不使用HTML表单的页面

页面通过AJAX发出POST请求,并且该页面没有带有 csrf_token 这将导致发送所需的CSRF cookie。

解决方案:使用 ensure_csrf_cookie() 在发送页面的视图上。

可重用应用中的CSRF保护

因为开发人员可以关闭 CsrfViewMiddleware ,Conrib应用程序中的所有相关视图都使用 csrf_protect 装饰器,以确保这些应用程序在CSRF环境下的安全性。建议希望获得相同保证的其他可重用应用程序的开发人员也使用 csrf_protect 装饰师在他们的视野中。