StackHawk
Hamburger Icon

Django CSRF Protection
Guide: Examples and
How to Enable

stackhawk

StackHawk|June 15, 2021

This guide dives into cross-site request forgery (CSRF), how it works, and why you should care. By walking you through examples in Django, we'll show you how to prevent them.

Sometimes it’s easier to build your website without thinking about security. And many times, perhaps due to tight timeframes, you might forget the need to factor in basic security measures. 

It’s arguably easier to build websites or web apps without paying much attention to security and internet request protocols. But then, to what end? 

  • Risking security hacks?

  • Risking attack from malicious requests?

  • Exposing your users to dangers from internet fraudsters?

For the good of your apps and your users, it makes sense to learn about security attacks in general — and to understand the need for and use of security measures for all your web applications in particular.  

Many popular security attacks exist on the web. The cross-site request forgery (CSRF) is one of them.  

In Django, there are several ways to prevent CSRF attacks.

In Django, there are several ways to prevent CSRF attacks. And for Django developers, Django’s measures against CSRF attacks are worth paying attention to. 

In this post, we’ll talk about what CSRF is and how it works. Then, we’ll walk you through examples in Django and how to prevent them.

What Exactly Is CSRF?

CSRF (or XSRF) is also known as cross-site request forgery. As the name suggests, CSRF is a kind of attack on sites mostly done by other (malicious) sites, or sometimes by a (malicious) user on the site.  

Typically, there are many cases where a site would require a user to fill in data from another website on behalf of that particular user. An example is how a lot of blogs use Disqus to power their commenting systems. To comment in that particular blog, the blog requires that you log in to Disqus first. This is a basic use of a CDN (content delivery network), and this example is a legitimate cross-site request. 

CSRF attacks usually rely on the user's identity. So what happens when a user visits a malicious website? That site sends hidden forms of some JavaScript XMLHttpRequest. That request uses the credentials of a user (one who visited their malicious site) to do some actions on another website that trusts the user’s browser or identity. 

A site usually identifies authenticated users by saving cookies with headers and content that represent that particular user in their browsers. Attackers use this to access the user’s credentials to perform their attacks.

An Example: Frank and the Bank

Some unknown attacker wants to access Frank’s bank account and steal his money. What happens if Frank’s bank is vulnerable to CSRF?  

To transfer cash, Frank has to use a particular URL that's saved to his browser, such as http://example_a_bank.com/app/service/transfer?amount=20000&destination=example_b_bank&accountNumber=9567265100. 

The transfer is successful. Then the browser saves a cookie session with Frank’s credentials, and Frank moves on.

The unknown attacker has a malicious website that Frank has probably innocently clicked. As a result, the attacker has placed an HTML code in the malicious website that looks like this:

<img src="http://example_a_bank.com/app/service/transfer?amount=60000&destination=example_c_bank&accountNumber=9017265100" 
width="0px" height="0px">

Now when Frank visits the malicious website, the browser will think it's loading or processing an image link. It will issue a GET request to fetch the picture, but it will also send a request to Frank’s bank to transfer $60,000 to the attacker’s specified bank account. The actual bank still has a cookie session saved in Frank’s browser. Therefore, the bank's systems think this is a real request and process it. 

This is a very serious vulnerability! How can banks and other businesses avoid this? Let’s dive into how to enable CSRF protection in Django.

How can banks and other businesses avoid vulnerabilities? Let's dive into how to enable CSRF protection in Django.

CSRF in Django

Powered by Python, Django is a free and open-source web framework that allows you to develop secure and maintainable websites in no time. Experienced developers built Django with the aim of reducing the unnecessary hassles of web development. This way, you can focus on building without having to reinvent the wheel. It's suitable for back-end and front-end development. 

Middleware in Django is a set of functions that run during request and response processes. And in Django, there’s CSRF middleware that helps protect against CSRF attacks in Django apps. 

When you start a Django project, you’ll see in your settings.py file that the middleware has been activated by default.

'django.middleware.csrf.CsrfViewMiddleware'

How to Use Django's CSRF Middleware

Step 1

You need to add django.middleware.csrf.CsrfViewMiddleware in the settings.py file to enable it. 
By default, Django already has this enabled, as in the following example:

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware', #Csrf Middleware is added
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
]

However, if you override these settings, you can decide to put django.middleware.csrf.CsrfViewMiddleware before any middleware that assumes that CSRF attacks have been dealt with.

Step 2

Django has a template tag that makes it easy to use CSRF protection:

{% csrf_token %}

In a template that uses the POST form, use the csrf_token inside the <form> element.

<form action="" method="post">
    {% csrf_token %}
</form>

The CSRF Decorator Method

Do you want to use CSRF protection on a particular view? Then csrf_protect decorator is right for you. It’s got the same functionality as the CsrfViewMiddleware, but it works only on the views you assign it to.

from django.shortcuts import render 
from django.views.decorators.csrf import csrf_protect 

@csrf_protect 
def user(request):     
    user = {}     
    # ...     
    return render(request, "user_view.html", user)

NOTE: It's better to use CsrfViewMiddleware as an overall protection. If you forget to add the decorator to your views, it'll create security issues. You must use it on the views that assign CSRF tokens to the output and the ones that accept data from the POST form. 

You can also use CsrfViewMiddleware as your blanket protection and still decide which view gets to use it. Like this:

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

@csrf_exempt
def user(request):
    return HttpResponse('CSRF token is being exempted here!')

The csrf_exempt decorator marks the view and exempts it from the protection the middleware ensures on all views.

Other Decorator Methods

Here are some other methods you might find useful.

csrf_exempt(view): It marks a view as exempt from the CSRF protection. 

requires_csrf_token(view): This ensures that the template tag csrf_token works. Its function is similar to crsf_protect, but it doesn't reject an incoming request. 

ensure_csrf_cookie(views): This enforces a view to set a CSRF cookie, even if the csrf_token template tag isn't used.

How Does the CSRF Token Work?

The CSRF token is like an alphanumeric code or random secret value that's peculiar to that particular site. Hence, no other site has the same code. 

In Django, the token is set by CsrfViewMiddleware in the settings.py file. 

A hidden form field with a csrfmiddlewaretoken field is present in all outgoing requests. When you submit a form to the server that it didn’t send to you, the server won’t accept it—unless it has the CSRF token that matches the one the server recognizes. 

The server has its own CSRF token. That's what it sends, along with a form to the client for protection of information. 

All incoming requests must have a CSRF cookie, and the csrfmiddlewaretoken field must be present and correct. Otherwise, the user will get a 403 error.

Enabling Django CSRF Protection With Django REST and React

What about cases where you’re using Django REST and a separate front-end framework, such as React? You'll definitely want to ensure that you have CSRF protection enabled as well. Here’s how.

Using React Forms to Render a CSRF Token

Django templates allow you to easily include:

{% csrf_token %} 

inside forms. However, in React, you’ll have to go the longer route to render it yourself.

Step 1

You have to fetch the csrf token from Django's csrf_token cookie. But this will be set only if the CSRF middleware is enabled in Django. 

In Django’s official documents, here’s the way to get the token in JavaScript:

function getCookie(name) {
    let cookieValue = null;

    if (document.cookie && document.cookie !== '') {
        const cookies = document.cookie.split(';');
        for (let i = 0; i < cookies.length; i++) {
            const cookie = cookies[i].trim();

            // 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;
}

You can now retrieve the token as shown below:

const csrftoken = getCookie('csrftoken');

Step 2

You can then create a global csrftoken.js file that has the following:

import React from 'react';

const csrftoken = getCookie('csrftoken');

const CSRFTOKEN = () => {
    return (
        <input name="csrfmiddlewaretoken" value={csrftoken} type="hidden" />
    );
};

export default CSRFTOKEN;

It’s now easier to import and include it inside as many forms as possible:

import CSRFTOKEN from './csrftoken';

class SampleForm extends Component {
    render() {
        return (
                 <form action="/" method="POST">
                        <CSRFTOKEN />
                        ...
                 </form>
        );
    }
}

export default SampleForm;

Sending a FETCH Request With Django CSRF Token in React

Also, you can also now easily send a React FETCH request while assigning the csrf_token X-CSRFToken header: 

 fetch(url, {
    credentials: 'include',
    method: 'POST',
    mode: 'same-origin',
    headers: {
      'Accept': 'application/json',
      'Content-Type': 'application/json',
      'X-CSRFToken': csrftoken
    },
    body: {}
   })
  }

There! You’ve been able to include Django’s csrf_token in React.

Watch a demo to see StackHawk in flight

Conclusion

You've now learned what CSRF protection is and how to enable it in Django. This means you're one step ahead of security attacks and malicious attempts against your users. 

To learn even more, check out this post. You can also review this example on how other tools integrate CSRF tokens to perform automated authentication tests from StackHawk

This post was written by Juan Pablo Macias Gonzalez. Juan is a computer systems engineer with experience in backend, frontend, databases and systems administration.


StackHawk  |  June 15, 2021

Read More

Guide to Security in Django

Guide to Security in Django

What is Cross-Site Request Forgery (CSRF)?

What is Cross-Site Request Forgery (CSRF)?

Django Broken Object-Level Authorization Guide: Examples and Prevention

Django Broken Object-Level Authorization Guide:Examples and Prevention