3
votes

There are a lot of stack overflow questions on this topic that I have been going through and have not found what I'm looking for but I apologize if this is a repeat of another question.

My problem is that I believe I have set up CSRF cookies correctly for my django project but I get the following error (which begs to differ):

Forbidden (403)

CSRF verification failed. Request aborted. Help

Reason given for failure:

CSRF token missing or incorrect.

Using firebug, I can verify that I am receiving a csrf token from my server, and that my .post is including that same csrf token in the request. I know that my browser is not the problem because I can view my browser's cookies and verify that the same csrf token is stored in the browser.

views.py:

from django.views.decorators.csrf import ensure_csrf_cookie, csrf_exempt
from django.shortcuts import render_to_response
from django.http import HttpResponse
from django.contrib.auth import authenticate, login, logout
from django.contrib.auth.decorators import login_required
from django.template import RequestContext

def base_view(request):
    #Determine data for page

    return render_to_response('template.html',{'important_data':important_data}, context_instance=RequestContext(request))

@login_required
def ajax_view(request):
    if request.method == u'POST':
        #Do some things with database
        return HttpResponse('')

template.html

<!DOCTYPE html>
<html>
<head>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js" ></script>
<script src="{{ STATIC_URL }}JS/jquery.js"></script>
</head>
<body>
{% csrf_token %}
<div class="ajax_trigger"></div>
</body>
</html>

jquery.js

$(document).ready(function(){
    $(".ajax_trigger").click(function(){
            $.post("http://127.0.0.1:8000/ajax",{data:"test"},function(){},'json'); 
    });
});

//code recommended at https://docs.djangoproject.com/en/dev/ref/contrib/csrf/
function getCookie(name) {
    var cookieValue = null;
    if (document.cookie && document.cookie != '') {
        var cookies = document.cookie.split(';');
        for (var i=0; i<cookies.length; i++) {
            var cookie = jQuery.trim(cookies[i]);
            // Does this cookie string begin with the name we want?
            if (cookie.substring(0, name.length+1) == (name + '=')) {
                cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                break;
            }
        }
    }
    return cookieValue;
}
var csrftoken = getCookie('csrftoken');

function csrfSafeMethod(method) {
    //these HTTP methods do not require CSRF protection
    return(/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
function sameOrigin(url) {
    // test that a given url is a same-origin URL
    // url could be relative or scheme relative or absolute
    var host = document.location.host; // host + port
    var protocol = document.location.protocol;
    var sr_origin = '//' + host;
    var origin = protocol + sr_origin;
    //Allow absolute or scheme relative URLs to the same origin
    return (url == origin || url.slice(0, origin.length + 1) == origin + '/') || (url == sr_origin || url.slice(0, sr_origin.length + 1) == sr_origin + '/') ||
    // or any other URL that isn't scheme relative or absolute i.e relative
    !(/^(\/\/|http:|https:).*/.test(url));
}

$.ajaxSetup({
    beforeSend: function(xhr, settings) {
        if (!csrfSafeMethod(settings.type) && sameOrigin(settings.url)) {
            //send the token to same-origin, relative URLs only.
            //send the token only if the method warrants CSRF protection
            //using the CSRFToken value acquired earlier
            xhr.setRequestHeader("X-CSRFToken", csrftoken);
        }
    }
});

settings.py snippet:

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',
    # Uncomment the next line for simple clickjacking protection:
    # 'django.middleware.clickjacking.XFrameOptionsMiddleware',
)

If I add @csrf_exempt to my ajax_view then the response to the post request will be successful. But doesn't this defeat the purpose of having csrf protection in the first place?

I don't think that Django CSRF check failing with an Ajax POST request has the answer I'm looking for because it seems to apply only to an old version of django.

Thank you to anyone who can try to help point me in the right direction!

UPDATE: This configuration worked on django 1.4 but on django 1.5+ I am having some problems. After reviewing the Django 1.5 release notes (https://docs.djangoproject.com/en/dev/releases/1.5/#miscellaneous) I found this: "The csrf_token template tag is no longer enclosed in a div. If you need HTML validation against pre-HTML5 Strict DTDs, you should add a div around it in your pages." Does that mean anything to anyone? Also, I noticed in django's documentation (https://docs.djangoproject.com/en/dev/ref/contrib/csrf/) that for a situation like mine where a page uses ajax without a form the solution is to "use ensure_csrf_cookie() on the view that sends the page." I've tried this but I haven't noticed a difference.

If anyone is still reading this, thank you.

UPDATE 2: I will be quite embarrassed if someone finds a mistake with my urls of all things. Might have to register for djangocon with a fake name or something...

urls.py

from django.conf.urls import patterns, include, url
from mysite.views import *


# Uncomment the next two lines to enable the admin:
from django.contrib import admin
admin.autodiscover()

urlpatterns = patterns('',
    url('^$', base_view, {'category' : 'general'}, name='home'),
    url('^ajax$', ajax_view),

    # Uncomment the next line to enable the admin:
    url(r'^admin/', include(admin.site.urls)),
)
1
Looks like your POST url is not same as from where you received the csrftoken. That might cause the invalid token.Rohan
my post url is "127.0.0.1:8000/ajax" and I am receiving the csrf token from "127.0.0.1:8000". I agree that the urls are different but why would that cause the token to be invalid? Do you have any links to further resources that explain why that is necessary?johnmic07

1 Answers

3
votes

try to add ensure_csrf_cookie decorator

from django.views.decorators.csrf import ensure_csrf_cookie

@ensure_csrf_cookie
def ajax_view(request):

and you don't need {% csrf_token %} in template.html