装饰者方法
与其将CsrfViewMiddleware添加为一揽子保护,您可以在需要保护的特定视图上使用具有完全相同功能的csrf_protect修饰器。 它必须用于在输出中插入CSRF标记的视图以及接受POST表单数据的视图中。 (这些通常是相同的视图功能,但并不总是)。
不建议使用装饰器,因为如果您忘记使用装饰器,则会出现安全漏洞。 使用两者的“腰带和大括号”策略是好的,并且会产生最小的开销。
使用:
from django.views.decorators.csrf import csrf_protect
from django.shortcuts import render
@csrf_protect
def my_view(request):
c = {}
# ...
return render(request, "a_template.html", c)
如果您使用基于类的视图,则可以参考Django文档中装饰基于类的视图。
拒绝的请求
默认情况下,如果传入的请求未通过CsrfViewMiddleware执行的检查,则会向用户发送“403 Forbidden”响应。 这通常只有在存在真正的跨站点请求伪造时才会出现,或者由于编程错误,CSRF令牌未包含在POST表单中时才会看到。
但是,错误页面并不是非常友好,因此您可能希望提供自己的视图来处理这种情况。 为此,只需设置CSRF_FAILURE_VIEW设置即可。
怎么运行的
CSRF保护基于以下几点:
-
一个CSRF cookie,被设置为一个随机值(会话独立的随机数,因为它被称为),其他站点将无法访问。该cookie由CsrfViewMiddleware设置。这意味着永久性的,但由于没有办法设置永不过期的cookie,因此每发送一个名为django.middleware.csrf.get_token()的响应都会发送它(该函数在内部用于检索CSRF标记) 。
-
在所有传出的POST表单中都存在名称为“csrfmiddlewaretoken”的隐藏表单字段。该字段的值是CSRF cookie的值。该部分由模板标签完成。
-
对于所有未使用HTTP GET,HEAD,OPTIONS或TRACE的传入请求,必须存在CSRF cookie,并且“csrfmiddlewaretoken”字段必须存在且正确。如果不是,用户将得到403错误。此检查由CsrfViewMiddleware完成。
-
另外,对于HTTPS请求,严格的引用者检查由CsrfViewMiddleware完成。这是必要的,以解决使用会话独立随机数时在HTTPS下可能发生的中间人攻击,这是由于HTTP'Set-Cookie'头部(不幸地)被与客户通话的客户接受网站在HTTPS下。 (Referer检查没有针对HTTP请求完成,因为Referer头部在HTTP下不够可靠)。这确保只有源自您网站的表单才能用于POST数据。
-
它蓄意忽略GET请求(以及RFC 2616定义为“安全”的其他请求)。这些请求不应该有任何潜在的危险副作用,所以对GET请求的CSRF攻击应该是无害的。 RFC 2616将POST,PUT和DELETE定义为“不安全”,并且所有其他方法都被认为是不安全的,以获得最大程度的保护。
高速缓存
如果模板使用csrf_token模板标记(或者以其他方式调用get_token函数),则CsrfViewMiddleware将向响应添加一个cookie和一个Vary:Cookie标头。 这意味着,如果按照指示使用中间件(中间件UpdateCacheMiddleware先于所有其他中间件),则中间件将与缓存中间件配合良好。
但是,如果您在各个视图上使用缓存装饰器,则CSRF中间件还不能设置Vary头或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):
...
如果您使用基于类的视图,则可以参考Django文档中装饰基于类的视图。
测试
CsrfViewMiddleware通常会成为测试视图函数的一大障碍,因为CSRF令牌必须随每个POST请求一起发送。 出于这个原因,Django的测试HTTP客户端已被修改为在请求上设置一个标志,放松中间件和csrf_protect修饰器,以便它们不再拒绝请求。 在其他方面(例如发送cookies等),它们的行为是相同的。
如果出于某种原因希望测试客户端执行CSRF检查,则可以创建一个强制执行CSRF检查的测试客户端实例:
>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)
限制
站点内的子域将能够在整个域的客户端上设置Cookie。通过设置cookie并使用相应的令牌,子域将能够规避CSRF保护。避免这种情况的唯一方法是确保子域由受信任的用户控制(或至少无法设置cookie)。
请注意,即使没有CSRF,也存在其他漏洞,例如会话固定,使得给不受信任方的子域名是一个坏主意,而这些漏洞无法用当前浏览器轻易修复。
边缘案例
某些视图可能有不寻常的要求,这意味着它们不符合这里设想的正常模式。在这些情况下,许多公用设施可能会有用。下一节将介绍它们可能需要的场景。
CSRF公用事业
以下示例假设您使用的是基于功能的视图。如果您正在使用基于类的视图,则可以参考Django文档中装饰基于类的视图。
django.views.decorators.csrf.csrf_exempt(view)
大多数意见需要CSRF保护,但有一些则不需要。不是禁用中间件并将csrf_protect应用于所有需要它的视图,而是启用中间件并使用csrf_exempt()。
这个装饰器标志着一个视图被免除了中间件保证的保护。例:
from django.views.decorators.csrf import csrf_exempt
from django.http import HttpResponse
@csrf_exempt
def my_view(request):
return HttpResponse('Hello world')
django.views.decorators.csrf.requires_csrf_token(view)
有些情况下,CsrfViewMiddleware.process_view可能在您的视图运行之前未运行 - 例如404和500处理程序 - 但您仍然需要表单中的CSRF标记。
通常情况下,如果CsrfViewMiddleware.process_view或csrf_protect等效物尚未运行,则csrf_token模板标记将不起作用。 视图装饰器requires_csrf_token可以用来确保模板标签的工作。 该装饰器与csrf_protect类似,但从不拒绝传入的请求。
例:
from django.views.decorators.csrf import requires_csrf_token
from django.shortcuts import render
@requires_csrf_token
def my_view(request):
c = {}
# ...
return render(request, "a_template.html", c)
也可能有一些视图不受保护,并已被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()
django.views.decorators.csrf.ensure_csrf_cookie(view)
这个装饰器强制视图发送CSRF cookie。 这将被使用的场景是,如果页面通过AJAX发出POST请求,并且该页面没有带有csrf_token的HTML表单,这会导致发送所需的CSRF cookie。 解决方法是在发送页面的视图上使用ensure_csrf_cookie()。
Contrib和可重复使用的应用程序
由于开发人员可能会关闭CsrfViewMiddleware,因此contrib应用程序中的所有相关视图都使用csrf_protect修饰器来确保这些应用程序对CSRF的安全性。 建议其他需要相同保证的可重用应用程序的开发人员也在其视图上使用csrf_protect装饰器。
CSRF设置
可以使用多种设置来控制Django的CSRF行为:
- CSRF_COOKIE_AGE
- CSRF_COOKIE_DOMAIN
- CSRF_COOKIE_HTTPONLY
- CSRF_COOKIE_NAME
- CSRF_COOKIE_PATH
- CSRF_COOKIE_SECURE
- CSRF_FAILURE_VIEW
有关这些设置的更多信息,请参阅附录D.
网友评论