【问题标题】:Using Django's CSRF-protection on views cached by Varnish对 Varnish 缓存的视图使用 Django 的 CSRF 保护
【发布时间】:2011-09-11 15:03:44
【问题描述】:

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

所以有两个挑战:

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

  2. 默认情况下,我的 Varnish 会去除所有 cookie,我怎样才能轻松地让它去除除 csrftoken cookie 之外的所有 cookie?我是否必须设置特定的 CSRF_COOKIE_DOMAIN?

【问题讨论】:

    标签: django csrf varnish


    【解决方案1】:

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

    诀窍是使用 varnish 支持的ESI。我们获取 CSRF sn-p 并将其粘贴到自己的页面中,通过 varnish 时通过 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',
                ],
            }
        }
    ]
    

    清漆配置

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

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


    现在您可以像使用普通的{% csrf_token %} 标签一样简单地使用它:

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

    设置起来相当麻烦,但是一旦设置好,您就再也不用看它了。

    【讨论】:

    • 那么 CSRF cookie 呢? Django 检查 CSRF 隐藏表单字段和 CSRF cookie。这个方案只解决了隐藏域的问题,不是吗?
    • @srus +1,CSRF Coo​​kie 怎么样?那是问题所在,而不是隐藏字段输入。 Varnish 默认不缓存输入的隐藏令牌!
    【解决方案2】:

    在视图上使用 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

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

    【讨论】:

    • 我想澄清一下你在 1) “设置 django 以发送 CSRF cookie 而不是使用隐藏字段”的意思——有没有办法做到这一点?我的理解是您需要在所有表单中包含 {% csrf_token %} 标记(这会创建隐藏字段)。
    • 我认为这个解决方案不可行。 Django CSRF 保护通过设置 cookie 并检查其值是否与隐藏字段的值匹配来工作。换句话说,它需要一个隐藏字段和 cookie 来为每个请求而变化。
    • @Eli:但 Varnish 只需要一个 cookie 即可。
    【解决方案3】:

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

    以及向 Varnish 添加例外。确保带有表单的视图和正在发布数据的视图都使用装饰器。

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2012-03-28
      • 2012-06-28
      • 2013-02-13
      • 1970-01-01
      • 2014-08-05
      • 2020-04-14
      • 2020-06-07
      相关资源
      最近更新 更多