django-csrf django
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
There are a couple of methods of passing CSRF validation:
csrftoken
CSRF cookie which will contain the CSRF token, which Django will use to validate the requestX-CSRFToken
HTTP headerWhen you do your request in curl
, you are making a request to a page which Django will validate with CSRF, however you are not passing the CSRF token to it, so that Django cannot validate the request.
I don't know how to use curl, so please forgive me that I don't know how to do that in curl but the following steps should help you in solving the issue:
1 Step one is to get the csrf token:
You can do that by first requesting using GET
the url which list
view is responsible for. That will return a HTML document, which will contain the content of what {% csrf_token %}
returned. You can parse that segment and store the token value from there.
Second method is to add ensure_csrf_cookie
decorator to the list
view. That decorator will make sure that view will always return a csrftoken
cookie with the request. Then you still have to make a GET
request, however instead of parsing the result of {% csrf_token %}
, you can just get the csrf token from the returned cookie.
This step will provide you with the value of the csrf token which you will use in the next step.
2 Now that you will have the csrf toke, when making the POST
request, you will also pass additional X-CSRFToken
header with the value of the token you got in previous step. Passing the token will allow Django to validate the request and hence process the uploaded file.
Following these steps will allow you to make csrf-valid requests. Please for this approach it does not matter if the request is made using HTTP or HTTPS. HTTPS provides assurance of data-integrity, data-secrecy and validates the server authenticity. It does not provide client validation to the server, which is why CSRF is applicable for both HTTP and HTTPS.
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/
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With