未设置CSRF Coo​​kie或在django网站上上传https的CSRF验证失败

时间:2012-11-02 17:56:51

标签: django django-csrf

与周围的人一样,我使用CSRF和Django时遇到了很多问题。

以下是背景:
  - 我创建了一个https网站,用户可以上传文件   - 我已经使用Django 1.4.2来创建这个网站了   - 我已经创建了一个app * file_manager *来做我想要的事情   - 我只是通过管理员页面使用此应用

如果我在django.middleware.csrf.CsrfViewMiddleware的{​​{1}}中停用了MIDDLEWARE_CLASSES,那么一切正常。
我可以在Debian Squeeze下的命令行中使用cURL在我的模板上上传文件,文件命中服务器,没问题。
但是,它似乎并不安全。

所以我启用了settings.py 它不再起作用了。我在CSRF验证方面遇到了各种错误。

我相信我已经消除了通常的嫌疑人(我希望至少):   - django.middleware.csrf.CsrfViewMiddleware
  - {% csrf_token %}
  - settings.py

中的RequestContext

您将在下面找到参与此过程的所有文件(我希望):

views.py

CsrfViewMiddleware

list.html

from django.http import HttpResponse, HttpResponseRedirect, Http404
from django.template import Context, loader, RequestContext
from django.shortcuts import render, get_object_or_404, redirect, render_to_response
from django.core.urlresolvers import reverse

from django.core.context_processors import csrf
from django.views.decorators.csrf import csrf_exempt

from file_manager.models import MyClass
from file_manager.forms import MyClassForm

def index(request):
    latest_file_list = MyClass.objects.order_by('-name')[:5]
    context = Context({
        'latest_file_list': latest_file_list,
    })
    return render(request, 'file_manager/index.html', context)

def list(request):
    # Handle file upload
    if request.method == 'POST':
        form = MyClassForm(request.POST, request.FILES)
        if form.is_valid():
            newdoc = MyClass(name='testupl', filefield = request.FILES['docfile'], uploader='fcav')
            newdoc.save()

            # Redirect to the document list after POST
            return HttpResponseRedirect(reverse('file_manager.views.list'))

    else:
        form = MyClassForm() # A empty, unbound form

    # Load documents for the list page
    documents = MyClass.objects.all()

    # Render list page with the documents and the form
    con = {'documents': documents, 'form': form}
    con.update(csrf(request))
    return render_to_response(
        'list.html',
        con,
        context_instance=RequestContext(request)
    )

这是我的卷曲请求:

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>Minimal Django File Upload Example</title>
    </head>
    <body>
    <!-- List of uploaded documents -->
    {% if documents %}
        <ul>
        {% for document in documents %}
            <li><a href="{{ document.filefield.url }}">{{ document.filefield.name }}</a></li>
        {% endfor %}
        </ul>
    {% else %}
        <p>No documents.</p>
    {% endif %}

        <!-- Upload form. Note enctype attribute! -->
        <form action="{% url list %}" method="post" enctype="multipart/form-data">{% csrf_token %}
            <p>{{ form.non_field_errors }}</p>
            <p>{{ form.docfile.label_tag }} {{ form.docfile.help_text }}</p>
            <p>
                {{ form.docfile.errors }}
                {{ form.docfile }}
            </p>
            <p><input type="submit" value="Upload" /></p>
        </form>
    </body>
</html>

我尝试在请求中发送Cookie时尝试了很多卷曲请求的变体,但现在我认为我的想法完全混淆了cURL如何处理上传。

我得到的全部是CSRF Coo​​kie未设置或CSRF验证失败。

如果有人知道cURL和Django足以告诉我如何在不禁用CSRF的情况下尝试在我的网站上传,我会非常感激。

此致 弗洛里安

2 个答案:

答案 0 :(得分:4)

有几种方法可以通过CSRF验证:

  • 传递csrftoken CSRF cookie,其中包含CSRF令牌,Django将使用该令牌验证请求
  • 将CSRF令牌传递为X-CSRFToken HTTP标头

当您在curl中执行请求时,您正在向Django将使用CSRF验证的页面发出请求,但是您没有将CSRF令牌传递给它,因此Django无法验证请求。 / p>

我不知道如何使用卷曲,所以请原谅我,我不知道如何在卷曲中做到这一点,但以下步骤应该可以帮助您解决问题:

1 第一步是获取csrf令牌:

您可以首先请求GET list视图负责的网址。这将返回一个HTML文档,其中包含{% csrf_token %}返回的内容。您可以解析该段并从那里存储令牌值。

第二种方法是将ensure_csrf_cookie decorator添加到list视图中。该装饰器将确保视图始终返回带有请求的csrftoken cookie。然后你仍然需要发出GET请求,但是不是解析{% csrf_token %}的结果,而是从返回的cookie中获取csrf标记。

此步骤将为您提供将在下一步中使用的csrf标记的值。

2 现在您将拥有csrf toke,在发出POST请求时,您还会传递额外的X-CSRFToken标头,其中包含您获得的令牌的值前一步。传递令牌将允许Django验证请求,从而处理上传的文件。


遵循这些步骤将允许您进行csrf有效请求。对于这种方法,请求是使用HTTP还是HTTPS进行无关紧要。 HTTPS可确保数据完整性,数据保密性并验证服务器的真实性。它不向服务器提供客户端验证,这就是CSRF适用于HTTP和HTTPS的原因。

答案 1 :(得分:1)

所以...我做到了......
经过了很多考验但我终于成功了...... 看起来我需要通过cURL发送:
  - 来自服务器的证书
  - 具有域名
的引用标头   - cookie csrftoken及其值
  - 带有csrf标记值的额外标头
  - 我要上传的文件

有点像这样:

curl --cacert /path/to/cert/apache.pem -e "https://domain.com" --cookie "csrftoken=[value]" -H "X-CSRFToken: [value]" -X POST -F "docfile=@/path/to/myfile/file.csv" https://domain.com/admin/list/