3

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

这里是上下文:
- 我创建了一个 https 网站,用户可以在其中上传文件
- 我使用 Django 1.4.2 创建这个网站
- 我创建了一个应用程序 *file_manager* 可以满足我的需求
- 我是仅通过管理员网址使用此应用

如果我禁用my django.middleware.csrf.CsrfViewMiddleware,一切正常。 我可以在 Debian Squeeze 下的命令行中使用 cURL 在我的模板上上传一个文件,该文件会到达服务器,没问题。 但是,似乎并不安全。MIDDLEWARE_CLASSESsettings.py

所以我启用django.middleware.csrf.CsrfViewMiddleware 了它不再起作用了。我收到有关 CSRF 验证的各种错误。

我相信我已经消除了通常的嫌疑人(至少我希望如此):
- {% csrf_token %}
- RequestContext
-CsrfViewMiddlewaresettings.py

您将在下面找到该过程涉及的所有文件(我希望):

视图.py

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)
    )

列表.html

<!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>

这是我的卷曲请求:

    curl --cacert /home/fcav/apache.pem --user admin:password
-e "https://domain.com/admin/" -X POST 
-F "docfile=@/home/fcav/Downloads/basket.csv"
https://domain.com/admin/file_manager/myclass/list/

我通过尝试在请求中发送 cookie 也尝试了 curl 请求的许多变体,但现在我认为我的想法完全混淆了 cURL 如何处理上传。

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

如果有人知道 cURL 和 Django 足以给我关于如何在不禁用 CSRF 的情况下尝试上传到我的网站的提示,我将非常感激。

问候,弗洛里安

4

2 回答 2

5

通过 CSRF 验证有两种方法:

  • 传递csrftoken包含 CSRF 令牌的 CSRF cookie,Django 将使用它来验证请求
  • 将 CSRF 令牌作为X-CSRFTokenHTTP 标头传递

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

我不知道如何使用 curl,所以请原谅我不知道如何在 curl 中执行此操作,但以下步骤应该可以帮助您解决问题:

1第一步是获取 csrf 令牌:

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

第二种方法是将ensure_csrf_cookie 装饰器添加到list视图中。该装饰器将确保视图将始终csrftoken随请求返回一个 cookie。然后您仍然需要发出请求,但是您可以从返回的 cookie 中获取 csrf 令牌GET,而不是解析 的结果。{% csrf_token %}

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

2现在您将拥有 csrf 令牌,在发出POST请求时,您还将传递X-CSRFToken带有您在上一步中获得的令牌值的附加标头。传递令牌将允许 Django 验证请求并因此处理上传的文件。


遵循这些步骤将允许您发出 csrf-valid 请求。请使用这种方法,无论请求是使用 HTTP 还是 HTTPS 发出的。HTTPS 提供数据完整性、数据保密性的保证并验证服务器的真实性。它不向服务器提供客户端验证,这就是 CSRF 适用于 HTTP 和 HTTPS 的原因。

于 2012-11-02T20:47:06.300 回答
1

所以...我做到了...
进行了很多测试,但我终于成功了...
看起来我需要通过 cURL 发送:
-来自服务器的证书
-带有域名的引用标头
-带有 csrftoken 的 cookie它的值
- 带有 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/
于 2012-11-05T09:30:38.257 回答