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

发布于 2024-11-14 09:45:55 字数 324 浏览 2 评论 0原文

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

所以有两个挑战:

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

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

I have a Django view with a form that uses CSRF protection. I want this view to be cached by Varnish when there is a normal GET request (since all users need the same form, no login).

So there are two challenges:

  1. How to cache this page in Varnish and not delivered cached/old versions of the csrf hidden field to the user? Is it at all possible to cache pages with CSRF field?

  2. My Varnish by default strips out all cookies, how could I easily make it strip all cookies, except the csrftoken cookie? And do I have to set a specific CSRF_COOKIE_DOMAIN?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

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

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

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>

设置起来相当麻烦,但一旦完成,您就不必再查看它了。

This is a couple of years late, but here's how I got around this problem recently.

The trick is to use ESI, which varnish supports. We take the CSRF snippet and stick it into its own page, including it via ESI when going through varnish, and directly otherwise (such as when running the local dev server).

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;

You also need to whitelist the csrf_esi.html page so it never gets cached and add set beresp.do_esi = true; inside the vcl_fetch function. I'd elaborate more on this, but I didn't set this part of the system up and am not 100% clear myself.


Now you can simply use it like you do the normal {% csrf_token %} tag:

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

It's quite a bit to set up, but once you do, you'll never have to look at it again.

那一片橙海, 2024-11-21 09:45:55

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

然而,正如您似乎已经猜到的那样,Django 确实提供了绕过此限制的机制,即 cookies。因此,在第二部分中,需要完成两件事:

  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

Using CSRF on a view essentially means that each render of the view is inherently different (even though only the value of one hidden field is changing). Caching doesn't work in such a scenario.

However, Django does provide mechanisms for getting around this limitation, namely cookies, as you seem to already have guessed. So on your second part, there's two things that need to be done:

  1. Set up Django to send CSRF cookies instead of using the hidden field. (See: https://docs.djangoproject.com/en/dev/ref/contrib/csrf/#s-caching)
  2. Make Varnish disregard the cookie Django sends. (See: http://www.varnish-cache.org/docs/trunk/tutorial/cookies.html)

You only need to set CSRF_COOKIE_DOMAIN in Django if the request will be coming from a different domain than where it is processed.

昵称有卵用 2024-11-21 09:45:55

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

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

我只在帖子视图上有@csrf_protect,它在本地测试工作正常,但是当我上线时,出现了 403 验证失败的问题,将装饰器添加到页面视图修复了此问题

I had similar issues using @csrf_protect and AJX, if anyone is using this decorator this might help

As well as adding exceptions to Varnish. Make sure that both the view with the form and the view that the data is being posted to use the decorator.

I only had @csrf_protect on the post view which worked fine testing locally but when i went live o got a 403 verification failed issue adding the decorator tot he page view fixed this

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文