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

And*_*s E 10 django csrf varnish

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

所以有两个挑战:

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

  2. 默认情况下,我的清漆会去掉所有的饼干,除了csrftoken饼干之外,我怎么能轻松地去掉所有的饼干?我是否必须设置特定的CSRF_COOKIE_DOMAIN?

Chr*_*att 8

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

但是,Django确实提供了绕过这种限制的机制,即cookie,正如您似乎已经猜到的那样.所以在你的第二部分,有两件事需要做:

  1. 设置Django以发送CSRF cookie而不是使用隐藏字段.(见:https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#s-caching)
  2. Make Varnish忽略了Django发送的cookie.(见:http://www.varnish-cache.org/docs/trunk/tutorial/cookies.html)

CSRF_COOKIE_DOMAIN如果请求来自不同的域而不是处理它,您只需要在Django中设置.

  • 我认为这个解决方案不可行。Django CSRF 保护的工作原理是设置 cookie 并检查其值是否与隐藏字段的值匹配。换句话说,它需要一个隐藏字段和 cookie 来根据每个请求而变化。 (3认同)

Ano*_*ous 7

这是迟了几年,但这是我最近解决这个问题的方法.

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

csrf_esi.html:

{% csrf_token %}
Run Code Online (Sandbox Code Playgroud)

csrf_token.html

{% if request.META.HTTP_X_VARNISH_USE_CACHE %}
<esi:include src="{% url 'esi_csrf_token' %}" />
{% else %}
{% include "csrf_esi.html" %}
{% endif %}
Run Code Online (Sandbox Code Playgroud)

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'),
]
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

settings.py

TEMPLATES = [
    {
        ...
        'OPTIONS': {
            ...
            'builtins': [
                'path.to.csrf_esi',
            ],
        }
    }
]
Run Code Online (Sandbox Code Playgroud)

清漆配置

set req.http.X-Varnish-Use-Cache = true;
Run Code Online (Sandbox Code Playgroud)

您还需要将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>
Run Code Online (Sandbox Code Playgroud)

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