8

I have a django server to upload files and when I use a browser, I can upload the file without problems.

But if I use the python-requests commands, it tells me CSRF verification failed. Request aborted. The python-requests code is as followed:

    # upload via HTTP
    file = {"docfile": open(fullfilename, "rb")}
    s = requests.Session()
    r = s.get(dhost)
    r = s.post(dhost, files=file)

If I execute my code, I get the code 403 and the error CSRF verification failed. Request aborted. Reason given for failure:

CSRF token missing or incorrect.

But if I look in the header I sent, I have the cookie set:

CaseInsensitiveDict({'Content-Length': u'84169', 
'Accept-Encoding': 'gzip, deflate, compress', 
'Accept': '*/*', 
'User-Agent': 'python-requests/2.0.1 CPython/2.7.3 Linux/3.6.11+', 
'Cookie': 'csrftoken=GOOIsG89i5oMCJO6594algTXooxoUeoL', 
'Content-Type': 'multipart/form-data; boundary=86ada00b4f6c41d5997293cce7a53b6b'})

Could you please tell me what I should do in order to have this to work?

Paolo
  • 20,112
  • 21
  • 72
  • 113
user2040597
  • 469
  • 3
  • 8
  • 21
  • Is this code being executed as a POST? If so, are you using a form ? You might have to add the `{% csrf_token %}` to the form. – karthikr Nov 27 '13 at 20:38

3 Answers3

19

It's actually all working fine, you just have to understand how csrf works. When you load the page in your browser, you get a csrf token inside {% csrf_token %}, So when you send the data to the server, you also send along the csrf token.

When you use requests, you're getting the cookie in the 'get' part, but you're not sending it along with your 'post'. without it, you're just sending a post request with no token at all, which means a CSRF verification error. To solve it, try this code:

file = {"docfile": open(fullfilename, "rb")}
s = requests.Session()
r1 = s.get(dhost)
csrf_token = r1.cookies['csrftoken']
r2 = s.post(dhost, files=file, data={'csrfmiddlewaretoken': csrf_token}, headers=dict(Referer=dhost))

If this is just for your own usage, you can disable csrf on that view using csrf_exampt:

@csrf_exempt
def my_view(request):
   ...whateva...

But note that this isn't a recommended solution if you plan to launch your server and open it to the public

yuvi
  • 18,155
  • 8
  • 56
  • 93
  • I would like to open it to public later on. The thing is that it works when I use chrome browser, I have the select file button then the upload button and if I hit this last one, the file is uploaded on my django webserver and I can find it in the folder on my server. So why isn't it working with the python-requests command or what other command should I use? – user2040597 Nov 27 '13 at 20:42
  • 1
    As I explained, when using the browser you're sending the csrf-token along with the post request. In the python-requests code, without the 'get' part, you're sending a tokenless request which returns an error – yuvi Nov 27 '13 at 20:44
  • I have updated my question because even if I do the get before I have an error 403... So I don't mind doing a get before but my problem is really that I can't post :(. – user2040597 Nov 27 '13 at 20:46
  • Yeah, I was actually surprised you said it was working with the 'get', but I thought maybe a different version of django or something... Anyway, the problem is still the same = you need to send the token from along with your request, wait I'll edit it in my answer to show you how... – yuvi Nov 27 '13 at 20:54
  • Sure thing, happy to have helped! =] – yuvi Nov 27 '13 at 22:11
3

If I use the python-requests commands, it tells me CSRF verification failed. But if I look in the header I sent, I have the cookie set:

  'Content-Length': u'84169', 
  'Accept-Encoding': 'gzip, deflate, compress', 
  'Accept': '/', 
  'User-Agent': 'python-requests/2.0.1 CPython/2.7.3 Linux/3.6.11+',
  'Cookie': 'csrftoken=GOOIsG89i5oMCJO6594algTXooxoUeoL',
  'Content-Type': 'multipart/form-data; boundary=86ada00b4f6c41d5997293cce7a53b6b

You have to send two things back to the server:

1) The csrftoken cookie.

2) The following form name/value pair:

"csrfmiddlewaretoken" = "csrf token here"

A requests session will take care of returning the cookie for you, but you have to add the form name/value pair:

sess = requests.Session()
r = sess.get(get_url)
my_csrf_token = r.cookies['csrftoken']

with open('myfile.txt') as f:
    r = sess.post(
        post_url,
        data = {
            "csrfmiddlewaretoken": my_csrf_token,
        },
        files = {"myfile": f}

)

print r.status_code
print r.text

When your django html template contains a csrf tag:

<form name="myMessage" 
      method="post" 
      class="signin" 
      action="/myapp/process_form/"
      enctype="multipart/form-data">

{% csrf_token %}

the csrf tag is replaced by a hidden form field:

<input type="hidden" 
       value="RTpun6OhlRehRRa2nAIcTtFJk5WuWsLg" 
       name="csrfmiddlewaretoken">

The hidden form field sends an additional name/value pair to the server along with all the other name/value pairs that the form creates. The name is "csrfmiddlewaretoken" and the value is the csrf token. django checks for the cookie as well as the name/value pair in the form data.

By the way, in order to get a csrf token for testing purposes, you can make a get request to a view that looks like this:

def myview(request):
    from django.middleware.csrf import get_token
    get_token(request)  #This causes django to set the csrftoken cookie in the response

    return HttpResponse('server received GET request')
7stud
  • 46,922
  • 14
  • 101
  • 127
-1

It depends what you're trying to do.

If you do not need CSRF verification you can use the csrf_exempt decorator.

Or you can create a new csrf_exempt view for only accessing through python-requests.

jpwagner
  • 553
  • 2
  • 8