簡體   English   中英

未設置CSRF Coo​​kie或在django網站上上傳https的CSRF驗證失敗

[英]CSRF Cookie not set or CSRF verification failed for https upload on django website

和周圍的人一樣,我使用CSRF和Django時遇到了很多問題。

以下是上下文:
- 我創建了一個https網站,用戶可以上傳文件
- 我用Django 1.4.2創建了這個網站
- 我創建了一個app * file_manager *來做我想要的
- 我只是通過管理員網址使用這個應用程序

如果我在settings.pyMIDDLEWARE_CLASSES中禁用django.middleware.csrf.CsrfViewMiddleware ,一切正常。
我可以在Debian Squeeze下的命令行中使用cURL在我的模板上上傳文件,文件命中服務器,沒問題。
但是,它似乎並不安全。

所以我啟用了django.middleware.csrf.CsrfViewMiddleware它不再起作用了。 我收到有關CSRF驗證的所有錯誤。

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

您將在下面找到該過程中涉及的所有文件(我希望):

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

list.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如何處理上傳。

我得到的是沒有設置CSRF Coo​​kie或CSRF驗證失敗。

如果有人知道cURL和Django足以給我提示如何在不禁用CSRF的情況下嘗試在我的網站上傳,我會非常感激。

此致,弗洛里安

有幾種方法可以傳遞CSRF驗證:

  • 傳遞將包含CSRF令牌的csrftoken CSRF cookie,Django將使用該令牌來驗證請求
  • 將CSRF令牌傳遞為X-CSRFToken HTTP標頭

當您在curl執行請求時,您正在向Django將使用CSRF驗證的頁面發出請求,但是您沒有將CSRF令牌傳遞給它,因此Django無法驗證請求。

我不知道如何使用卷曲,所以請原諒我,我不知道如何在卷曲中做到這一點,但以下步驟應該可以幫助您解決問題:

1第一步是獲取csrf令牌:

您可以通過首先請求使用GET list視圖負責的URL來實現。 這將返回一個HTML文檔,其中包含{% csrf_token %}返回的內容。 您可以解析該段並從那里存儲令牌值。

第二種方法是將ensure_csrf_cookie 裝飾器添加到list視圖中。 該裝飾器將確保視圖將始終返回帶有請求的csrftoken cookie。 然后你仍然需要發出GET請求,但是不是解析{% csrf_token %}的結果,而是可以從返回的cookie中獲取csrf標記。

此步驟將為您提供將在下一步中使用的csrf標記的值。

2現在您將擁有csrf toke,在發出POST請求時,您還將傳遞額外的X-CSRFToken標頭,其中包含您在上一步中獲得的標記的值。 傳遞令牌將允許Django驗證請求,從而處理上傳的文件。


執行這些步驟將允許您進行csrf有效請求。 對於這種方法,請求是使用HTTP還是HTTPS進行無關緊要。 HTTPS可確保數據完整性,數據保密性並驗證服務器的真實性。 它不向服務器提供客戶端驗證,這就是CSRF適用於HTTP和HTTPS的原因。

所以......我做到了......
做了很多測試,但我終於成功了......
看起來我需要通過cURL發送:
- 來自服務器的證書
- 帶域名的referer標頭
- 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/

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM