在Varnish缓存的视图上使用Django的CSRF保护

时间:2011-06-09 11:19:56

标签: django csrf varnish

我有一个Django视图,其中包含一个使用CSRF保护的表单。我希望在有正常的GET请求时由Varnish缓存此视图(因为所有用户都需要相同的表单,不需要登录)。

所以有两个挑战:

  1. 如何在Varnish中缓存此页面而不向用户提供缓存/旧版本的csrf隐藏字段?是否可以使用CSRF字段缓存页面?

  2. 我的清漆默认剥去了所有的cookie,除了csrftoken cookie之外,我怎样才能轻松剥离所有的cookie?我是否必须设置特定的CSRF_COOKIE_DOMAIN?

3 个答案:

答案 0 :(得分:8)

在视图上使用CSRF本质上意味着视图的每个渲染本质上都是不同的(即使只有一个隐藏字段的值正在改变)。缓存在这种情况下不起作用。

然而,Django确实提供了解决这个限制的机制,即cookie,正如您似乎已经猜到的那样。所以在你的第二部分,有两件事需要做:

  1. 设置Django以发送CSRF cookie而不是使用隐藏字段。 (见:https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#s-caching
  2. 让Varnish忽略Django发送的cookie。 (见:http://www.varnish-cache.org/docs/trunk/tutorial/cookies.html
  3. 如果请求来自与处理地址不同的域,则只需在Django中设置CSRF_COOKIE_DOMAIN

答案 1 :(得分:7)

这已经晚了几年,但这是我最近解决这个问题的方法。

诀窍是使用清漆支持的ESI。我们使用CSRF片段并将其粘贴到自己的页面中,包括通过ESI时通过清漆,以及其他方式(例如运行本地开发服务器时)。

csrf_esi.html:

{% csrf_token %}

csrf_token.html

{% if request.META.HTTP_X_VARNISH_USE_CACHE %}
<esi:include src="{% url 'esi_csrf_token' %}" />
{% else %}
{% include "csrf_esi.html" %}
{% endif %}

urls.py

from django.conf.urls import url
from django.views.generic import TemplateView

urlpatterns = [
    ...
    url(r'csrf_esi.html', TemplateView.as_view(template_name="csrf_esi.html"), name='esi_csrf_token'),
]

csrf_esi.py

from django import template

register = template.Library()

@register.inclusion_tag('csrf_token.html', takes_context=True)
def csrf_token_esi(context):
    return context

settings.py

TEMPLATES = [
    {
        ...
        'OPTIONS': {
            ...
            'builtins': [
                'path.to.csrf_esi',
            ],
        }
    }
]

Varnish config

set req.http.X-Varnish-Use-Cache = true;

您还需要将csrf_esi.html页面列入白名单,以便永远不会被缓存,并在set beresp.do_esi = true;函数中添加vcl_fetch。我会详细说明这一点,但我没有设置系统的这一部分,而且我自己也不是100%清楚。

现在您可以像使用普通{% csrf_token %}标记一样使用它:

<form action="">
    {% csrf_token_esi %}
    <button type="submit">Push me</button>
</form>

设置起来相当多,但是一旦你做了,你就再也不用再看它了。

答案 2 :(得分:1)

我使用@csrf_protect和AJX时有类似的问题,如果有人使用这个装饰器,这可能有帮助

除了为Varnish添加例外。确保带有表单的视图和发布数据的视图都使用装饰器。

我在帖子视图上只有@csrf_protect,它在本地进行了很好的测试,但是当我上线时,得到了403验证失败的问题,添加了装饰器,页面视图修复了这个