Search code examples
djangodjango-csrf

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


As many people around, I've had a lot of problems using CSRF and Django.

Here is the context :
- I've created a https website where user can upload files
- I've used Django 1.4.2 to create this website
- I've created an app *file_manager* that does what I want
- I'm only using this app through the admin urls

If I disable django.middleware.csrf.CsrfViewMiddleware in the MIDDLEWARE_CLASSES of my settings.py, everything works just fine.
I can upload a file on my template with cURL in command line under Debian Squeeze, the file hits the server, no problem.
However, it seems it is not safe.

So I enabled django.middleware.csrf.CsrfViewMiddleware It doesn't work anymore. I get all kind of errors regarding CSRF verification.

I believe I've eliminated the usual suspects (I hope at least) :
- {% csrf_token %}
- RequestContext
- CsrfViewMiddleware in settings.py

You'll find below all the files (I hope) involved in the process :

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>

And here is my curl request :

    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/

I've tried many variations of the curl request by trying to send the cookie in the request as well, but now I think my mind is completely mixed up on how cURL handles the upload.

All I get is CSRF Cookie not set or CSRF verification failed.

If anyone knows cURL and Django enough to give me hints about how I can try to upload on my website without disabling CSRF, I would really appreciate it.

Regards, Florian


Solution

  • So... I made it...
    Took a lot of tests but I finally succeeded...
    Looked like I needed to send via cURL :
    - the certificate from the server
    - a referer header with the domain name
    - the cookie csrftoken with its value
    - an extra header with the csrf token value
    - the file I want to upload

    Kind of something like this :

    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/