Django rest framework ignore missing csrf tokens with httpie

I am using httpie to play with my api written in django 1.7 and django rest framework 2.4. I tried to delete an object today:

$ http DELETE :8000/api/items/8/ --verbose
DELETE /api/items/8/ HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate, compress
Content-Length: 0
Host: 127.0.0.1:8000
User-Agent: HTTPie/0.8.0


HTTP/1.0 204 NO CONTENT
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS
Content-Language: cs
Content-Length: 0
Date: Wed, 07 Jan 2015 21:47:06 GMT
Server: WSGIServer/0.1 Python/2.7.6
Vary: Accept, Accept-Language, Cookie

      

This was successful, although it required a CSRF token. When I try to remove an object from Chrome with the following code:

$.ajax({
    type: "DELETE",
    url: "http://127.0.0.1:8000/api/items/6/"
});

      

I am getting the following request:

DELETE /api/items/6/ HTTP/1.1
Host: 127.0.0.1:8000
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
Accept: */*
Origin: http://127.0.0.1:8000
X-Requested-With: XMLHttpRequest
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36
DNT: 1
Referer: http://127.0.0.1:8000/inventory
Accept-Encoding: gzip, deflate, sdch
Accept-Language: cs,en-US;q=0.8,en;q=0.6,es;q=0.4,pt;q=0.2,sk;q=0.2
Cookie: cc_csrf=bd9fbbc8f75cffa2e1e3d2c95c2185c5; _ga=GA1.1.2038400685.1386436341; __utma=96992031.2038400685.1386436341.1417173095.1417428975.79; __utmz=96992031.1409752584.3.1.utmcsr=(direct)|utmccn=(direct)|utmcmd=(none); __zlcmid=MpdRtV3vZuf3D9; djdt=hide; sessionid=kiihjh6m77jm8v9ol7xrryip89sny55i; csrftoken=FtnnEWPLhMh0CAGMRMH77nB0AAno93uW

      

Answer:

HTTP/1.0 403 FORBIDDEN
Date: Wed, 07 Jan 2015 21:57:40 GMT
Server: WSGIServer/0.1 Python/2.7.6
Vary: Accept, Accept-Language, Cookie
Content-Type: application/json
Content-Language: en
Allow: GET, PUT, PATCH, DELETE, HEAD, OPTIONS

{"detail": "CSRF Failed: CSRF token missing or incorrect."}

      

My settings:

REST_FRAMEWORK = {
    # Use hyperlinked styles by default.
    # Only used if the `serializer_class` attribute is not set on a view.
    'DEFAULT_MODEL_SERIALIZER_CLASS': 'rest_framework.serializers.HyperlinkedModelSerializer',

    # Use Django standard `django.contrib.auth` permissions,
    # or allow read-only access for unauthenticated users.
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
    ],

    'DEFAULT_FILTER_BACKENDS': ('rest_framework.filters.DjangoFilterBackend',),
    'DATETIME_FORMAT': "%B %d, %Y"
}
MIDDLEWARE_CLASSES = (
    'django.middleware.common.CommonMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'debug_toolbar.middleware.DebugToolbarMiddleware',
)

      

So my question is, what is the difference between sending a DELETE request using JS ajax and sending a request using http?

+3


source to share


3 answers


This is because CSRF validation is only done when authenticated using SessionAuthentication

(i.e. with a cookie sessionid

set to django.contrib.auth

):

If you are using SessionAuthentication

, you need to enable CSRF tokens valid for all operations POST

, PUT

, PATCH

or DELETE

. ( source )



I am assuming that you are using a different auth method for the HTTPie-submitted request and therefore CSRF validation does not apply there.

https://github.com/tomchristie/django-rest-framework/blob/master/tests/test_authentication.py

+5


source


When a request is made through a browser, it includes a token sessionid

in the header Cookie

. This header is automatically set by the browser and includes other cookies that have been set (for example, djdt=hide

using the Django Debug toolbar).

Cookie: ...; sessionid=kiihjh6m77jm8v9ol7xrryip89sny55i; ...

      

Because of this, Django automatically authenticates the request (just like it normally does), which launches theSessionAuthentication

Django REST framework provided. SessionAuthentication

requires the CSRF token to be validated, which is included in the csrftoken

cookie header and X-CSRFToken

to ensure that nothing suspicious happens.



This means that when making a request in the browser, you must set the header X-CSRFToken

. Django includes some useful code snippets for popular libraries in their CSRF documentation .

Now, when you make requests over HTTPie, you usually use a different form of authentication such as basic authentication . By default, the Django REST framework allows BasicAuthentication

and SessionAuhentication

, unless you override them , and most of the documentation expects you to use basic authentication.

HTTPie supports basic authentication through a parameter -a username:password

. This explains why you are not getting any permissions when making a request DELETE

, since without authentication you should get an error 403

. should not allow you to provide the request you provided without authentication. DjangoModelPermissionsOrAnonReadOnly

+2


source


IMAGE AND UPDATED

OK, apart from the explanations already mentioned, after all this we can conclude why Httpie allows DELETE but not Javascript:

1) Since you actually disabled your authentication, in theory all METHODS will be allowed from a separate HTTP call, so your Httpie works (as it does when using Curl) because Restframework doesn't require you to.

2) Ajax call from Javascript, however slightly different because you are using your browser's console to make the call that is actually in the browser session. In addition to this, your cookie has retained your previous GET CSRF token, which after making the Ajax call, the CSRF token was retrieved from Django / Restframework, which does not support MATCH (since the CSRF token will be automatically generated on every request So this is a WRONG question, and not * MISSING ** token.

Hence, as in my previous comment, deleting your browser cookie / using the Private session did indeed resolve the issue and successfully allow you to perform the Ajax DELETE style.

Hope this helps, and thanks for all the tips and tricks that lead me to this conclusion.

+1


source







All Articles