403 error when POSTing to a view with csrf protection

298 views
Skip to first unread message

Mike

unread,
Jun 24, 2012, 2:28:48 AM6/24/12
to django...@googlegroups.com
I realized that I was heading down the wrong track with my previous message to this mailing list so I studied the problem some more.  Now I am trying to send a POST request to a view and it gets rejected with a 403 error.  when I disable the csrf requirement using the @csrf_exempt decorator, the view works as expected.  the csrf token is being passed in the POST request, so I don't understand why else the request fails.  Any ideas?

Cal Leeming [Simplicity Media Ltd]

unread,
Jun 24, 2012, 9:15:40 AM6/24/12
to django...@googlegroups.com
Hi Mike,

Please provide some code examples, a traceback snippet and a bit more info.

Here are some great tips on how to do this:

Without this information, others on the list may find it much more difficult to help you.

Cal

On Sun, Jun 24, 2012 at 7:28 AM, Mike <mike...@gmail.com> wrote:
I realized that I was heading down the wrong track with my previous message to this mailing list so I studied the problem some more.  Now I am trying to send a POST request to a view and it gets rejected with a 403 error.  when I disable the csrf requirement using the @csrf_exempt decorator, the view works as expected.  the csrf token is being passed in the POST request, so I don't understand why else the request fails.  Any ideas?

--
You received this message because you are subscribed to the Google Groups "Django users" group.
To view this discussion on the web visit https://groups.google.com/d/msg/django-users/-/DDIIvexJoLMJ.
To post to this group, send email to django...@googlegroups.com.
To unsubscribe from this group, send email to django-users...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/django-users?hl=en.

Kurtis Mullins

unread,
Jun 24, 2012, 5:15:53 PM6/24/12
to django...@googlegroups.com
Hey Mike,

Make sure that you include this within your <form> block: {% csrf_token %}

Then, check the source code of that page to make sure the <input type="hidden" .../> CSRF token tag was included.

Finally, using a Javascript debugger (such as Firebug) check to make sure that your request sent, when clicking the "submit" button, included the csrf token.

Also, make sure your cache and sessions are setup correctly as that is what Django relies upon for using this protection.

Mike

unread,
Jun 25, 2012, 7:11:50 AM6/25/12
to django...@googlegroups.com
Hi Kurtis - 

The problem is that the client is not a web browser.  Its desktop application that I'm developing for Mac OS using the Cocoa frameworks.  The Django server will process user's data and return it to the user's desktop application.  The desktop app will communicate with the django server via https using JSON.  Right now I'm trying to set up authentication. I have set up authentication for Django before , in a traditional web app (in another project) and it all works as expected.  Now I'm trying to do it again using my desktop app as the client and I can't get authentication to work.  As Cal said, I'm probably not providing enough information for anyone to help me here, but I'll try again.

Here are the steps I'm trying to accomplish:

1) desktop app sends a GET request to a Django view.  the purpose of this is to get the csrf token that will be used for the rest of the session.  This django view function has the @ensure_csrf_cookie decorator to ensure that it will send the csrf token even though I'm not using a template and the template tag.

2) Desktop app sends a POST request with JSON in the body to log the user into the django app.  The json contains the user's userid and password. The request also contains the csrf cookie.

Steps 1 and 2 are using the same view function (below).  When the desktop app sends the POST request in step 2, I get the 403 error that I described previously, and the function is never called.  If I add the @csrf_exempt decorator, then I can get the view function to execute the if request.method == 'POST': block shown below.  It seems like the csrf cookie isn't being sent with the POST request, which is why I added the @csrf_exempt decorator and the print request statement in the code below.  that enables me to see the request object that is sent to the Django view.  the request does indeed contain the csrf cookie.  See the block of text ad the end of this message.

@ensure_csrf_cookie
@csrf_exempt
def login(request):
    print 'received login request using method ' + request.method
    if request.method == 'POST':
        # todo: login user
        print request
    
    else: # must be a GET.  return the csrf token
        c = {}
        c.update(csrf(request))
        return render_to_response('empty.html', c)


When client app performs the two steps described above, it calls this function twice, once with a GET and once with a POST.  in the POST, the request object is printed to stdout.  Here are the first few lines of the output showing that the csrf cookie is included with the request:

 GET:<QueryDict: {}>,
POST:<QueryDict: {u'{"user":"test_user","password":"test_password"}': [u'']}>,
COOKIES:{'csrftoken': 'qsl91ZDqVL5wirXlUwIYmu8ytTVES3nt'},
META:{'Apple_PubSub_Socket_Render': '/tmp/launch-ZubgcP/Render',
 'Apple_Ubiquity_Message': '/tmp/launch-BfYBfH/Apple_Ubiquity_Message',
 'COMMAND_MODE': 'unix2003',
 'CONTENT_LENGTH': '47',
 'CONTENT_TYPE': 'application/json',
 'CSRF_COOKIE': 'qsl91ZDqVL5wirXlUwIYmu8ytTVES3nt',
 'CSRF_COOKIE_USED': True,
 'DISPLAY': '/tmp/launch-EruXcM/org.x:0',
 'DJANGO_SETTINGS_MODULE': 'nsserver.settings',
 'EDITOR': 'nano',
 'GATEWAY_INTERFACE': 'CGI/1.1',
 'HOME': '/Users/mike',
 'HTTP_ACCEPT': 'application/json',
 'HTTP_ACCEPT_ENCODING': 'gzip, deflate',
 'HTTP_ACCEPT_LANGUAGE': 'en-us',
 'HTTP_CONNECTION': 'keep-alive',
 'HTTP_COOKIE': 'csrftoken=qsl91ZDqVL5wirXlUwIYmu8ytTVES3nt',
 'HTTP_HOST': '127.0.0.1:8000',


As I mentioned earlier, the purpose of the initial GET request is only to get the csrf cookie that will be used in the rest of the session.  Now I'm wondering I'm setting up the csrf token correctly in that block of code.



Melvyn Sopacua

unread,
Jun 25, 2012, 7:41:23 AM6/25/12
to django...@googlegroups.com
Hi Mike,

> POST:<QueryDict: {u'{"user":"test_user","password":"test_password"}':
> [u'']}>,
> COOKIES:{'csrftoken': 'qsl91ZDqVL5wirXlUwIYmu8ytTVES3nt'},
> 'CSRF_COOKIE': 'qsl91ZDqVL5wirXlUwIYmu8ytTVES3nt',
> 'CSRF_COOKIE_USED': True,
> 'HTTP_COOKIE': 'csrftoken=qsl91ZDqVL5wirXlUwIYmu8ytTVES3nt',
> 'HTTP_HOST': '127.0.0.1:8000',

Since this all looks sane, what's the different with a 'real browser'
(no disrespect to your desktop app :) )? I'm not seeing anything wrong
here, so it must be something subtle. Or is a 'real browser' rejected as
well?
--
Melvyn Sopacua


Melvyn Sopacua

unread,
Jun 25, 2012, 9:06:28 AM6/25/12
to django...@googlegroups.com
On 25-6-2012 13:11, Mike wrote:

> POST:<QueryDict: {u'{"user":"test_user","password":"test_password"}':
> [u'']}>,

Wait a second...
Where's your csrfmiddlewaretoken from the {% csrf_token %} field that
you put in your form?

This is the process:
- the cookie token is basically a lock
- the POST request resembles trying to open the door with that lock
- the formfield token is a key

No key, no open door.
Wrong key, no open door.
--
Melvyn Sopacua


Kurtis Mullins

unread,
Jun 25, 2012, 9:12:44 AM6/25/12
to django...@googlegroups.com
> POST:<QueryDict: {u'{"user":"test_user","password":"test_password"}':
> [u'']}>,

Wait a second...
Where's your csrfmiddlewaretoken from the {% csrf_token %} field that
you put in your form?

This is the process:
- the cookie token is basically a lock
- the POST request resembles trying to open the door with that lock
- the formfield token is a key

No key, no open door.
Wrong key, no open door.

+1
Looks like your CSRF Token is missing from the POST data. 

Mike

unread,
Jun 25, 2012, 9:20:43 AM6/25/12
to django...@googlegroups.com
Well, thats the thing, there is no {% csrf_token %} field in my form because I have no form.  The initial GET request is processed with this block of code:
    else:
        c = {}
        c.update(csrf(request))
        return render_to_response('empty.html', c)

empty.html is an empty text file. Also, the view function is decorated with @ensure_csrf_cookie to make sure that the response contains the csrf cookie even though I'm not using  {% csrf_token %} 

In writing all this I just noticed that I have two lines of code aimed at making sure the response contains the csrf token:
@ensure_csrf_cookie
and 
c.update(csrf(request))

It seems like I shouldn't need both, so maybe having both is breaking something.  I'll check on that.
Mike

Mike

unread,
Jun 25, 2012, 9:29:11 AM6/25/12
to django...@googlegroups.com
The csrf token is supposed to be part of the POST data (i.e. the message body)?  I thought it only needed to be in the header. Aha!  I will check the django docs on this.

Melvyn Sopacua

unread,
Jun 25, 2012, 9:36:46 AM6/25/12
to django...@googlegroups.com
On 25-6-2012 15:20, Mike wrote:
>
>
> On Monday, June 25, 2012 3:06:28 PM UTC+2, Melvyn Sopacua wrote:
>>
>> On 25-6-2012 13:11, Mike wrote:
>>
>>> POST:<QueryDict: {u'{"user":"test_user","password":"test_password"}':
>>> [u'']}>,
>>
>> Wait a second...
>> Where's your csrfmiddlewaretoken from the {% csrf_token %} field that
>> you put in your form?
>>
>> This is the process:
>> - the cookie token is basically a lock
>> - the POST request resembles trying to open the door with that lock
>> - the formfield token is a key
>>
>> No key, no open door.
>> Wrong key, no open door.
>> --
>> Melvyn Sopacua
>>
>> Well, thats the thing, there *is* no {% csrf_token %} field in my form
> because I have no form.

Then you can't protect the form either, which is what CSRF is for. The
token in the form is different each time and behind the scenes
associated with your cookie token. So that form token is only valid for
that cookie and only valid once.
--
Melvyn Sopacua


Reply all
Reply to author
Forward
0 new messages