Django Form Submission using jQuery and cookies to protect the csrf Token

I recently wrote about how to add csrf protection for jQuery Ajax submissions when using the ‘POST’ method.  We can use a similar method to protect forms while not exposing the csrf token.  When we set  ‘django.middleware.csrf.CsrfViewMiddleware’, in the MIDDLEWARE_CLASSES in ‘settings.py’, Django sets a csrf token as a cookie for each web page.  We can use the jQuery ‘.submit()’ method to read the cookie and add a hidden input field to the form prior to submission.  Django will process the form just as if you had included {% csrf_token %} in the form.  The benefit is that it is not easy to read the crsf token because it is stored in a cookie and browsers don’t expose cookies outside of the active page.

First, here is a code example of the simple but vulnerable way to add a csrf token to a Django form.

<form action="/" method="post">
    {% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="Category"/>
</form>

Now, we’ll change our form so that we can take advantage of the jQuery ‘.submit()’ method each time this form is submitted.  We are writing this using classes, so that we can write a site-wide ‘$(document).ready(function(){…}’ that takes action on these classes.  As a reminder, one can have more than one ‘$(document).ready(function(){…}’ function.  We use one in a javascript file that defines site-wide behaviors via classes and then one for each specific page that defines page specific behaviors.  Here is the updated form that does not expose the csrf token and makes use of site-wide classes.

<form action="/" method="post" class="YPForm">
    {{ form.as_p }}
    <input type="submit" value="Category" class="YPButton"/>
</form>

Now we can go to the site-wide javascript file and define the event handler that fires when forms of this class are submitted.  Please note that this code has a dependency on the ‘jquery.cookie.js’ library.  Also, please note that I am using C-style indentation to make everything fit onto this page.

function submitYPForm() {
    var csrfToken = "<input type='hidden' name='csrfmiddlewaretoken' 
                           value='" + $.cookie('csrftoken') + "' />"
    $(".YPButton").before(csrfToken);
}

$(document).ready(function(){
    // Enables setting csrf cookie into Ajax 'POST' header
    $.ajaxSetup({ beforeSend:setCookieInHeader });

    // Enables jQuery UI events (for CSS) on the buttons on the site
    $(".YPButton").button();

    // Submit event handler
    $(".YPForm").submit(submitYPForm);
});

I would recommend doing everything that really matters with https, but short of that, this is quite a bit more secure than exposing the the csrf token on your webpage.  Enjoy!

Comments are closed.