我有一个Django视图,其中包含一个使用CSRF保护的表单。我希望在有正常的GET请求时由Varnish缓存此视图(因为所有用户都需要相同的表单,不需要登录)。
所以有两个挑战:
如何在Varnish中缓存此页面而不向用户提供缓存/旧版本的csrf隐藏字段?是否可以使用CSRF字段缓存页面?
我的清漆默认剥去了所有的cookie,除了csrftoken cookie之外,我怎样才能轻松剥离所有的cookie?我是否必须设置特定的CSRF_COOKIE_DOMAIN?
答案 0 :(得分:8)
在视图上使用CSRF本质上意味着视图的每个渲染本质上都是不同的(即使只有一个隐藏字段的值正在改变)。缓存在这种情况下不起作用。
然而,Django确实提供了解决这个限制的机制,即cookie,正如您似乎已经猜到的那样。所以在你的第二部分,有两件事需要做:
如果请求来自与处理地址不同的域,则只需在Django中设置CSRF_COOKIE_DOMAIN
。
答案 1 :(得分:7)
这已经晚了几年,但这是我最近解决这个问题的方法。
诀窍是使用清漆支持的ESI。我们使用CSRF片段并将其粘贴到自己的页面中,包括通过ESI时通过清漆,以及其他方式(例如运行本地开发服务器时)。
{% csrf_token %}
{% if request.META.HTTP_X_VARNISH_USE_CACHE %}
<esi:include src="{% url 'esi_csrf_token' %}" />
{% else %}
{% include "csrf_esi.html" %}
{% endif %}
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'),
]
from django import template
register = template.Library()
@register.inclusion_tag('csrf_token.html', takes_context=True)
def csrf_token_esi(context):
return context
TEMPLATES = [
{
...
'OPTIONS': {
...
'builtins': [
'path.to.csrf_esi',
],
}
}
]
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验证失败的问题,添加了装饰器,页面视图修复了这个