StackHawk
Hamburger Icon

Django CORS Guide:
What It Is and How to Enable It

stackhawk

StackHawk|April 30, 2021

Learn more about Django CORS, what it is, why you should use it and how to enable it in your Django server.

Django is a Python web framework that allows rapid web application development. Apps developed in Django may need to interact with other applications hosted on different domains (or even just different ports). For these requests to succeed, you’ll need to use cross-origin resource sharing (CORS) in your server.

Luckily, in Django there’s already a module that’s easy to install and configure to allow CORS requests and avoid errors. So, if you want to know more about CORS and how to enable it in your Django server, be sure to keep reading.

What Is CORS?

CORS is a mechanism to allow interaction with resources hosted on different domains. For instance, one of the most common scenarios to apply it is with Ajax requests.

In order to illustrate how CORS works, let’s assume you have a web application that lives in domain.com. But, to save user information, the app calls an API hosted in another URL—for example, api.domain.com. So, when a request to save data is sent to api.domain.com, the server evaluates the requests based on its headers and the request’s source.

If you allow the URL domain.com in the server, it will provide the proper response. If the domain is not allowed, the server provides an error. This information exchange occurs using HTTP headers.

Errors Involving CORS

CORS is a security feature that web clients (browsers) implement that can make requests to a specific server to fail. Some  possible server responses may include

  • An unauthorized status (403)

  • An error in a preflight request indicating which URLs can send CORS requests

As a clarification, a preflight request is a petition that browsers send to the server to discover what HTTP methods it accepts in requests. Then, the server can return an error status and a list of CORS-enabled URLs. If the server doesn’t include the domain making the request, the browser won’t even perform the actual data request.

As a rule of thumb, if you’re dealing with different domains, remember to be on the lookout for CORS issues. Also remember that using a different HTTP protocol or even a different port counts as a different domain. But there’s no need to worry, as current browsers’ tools are very helpful when diagnosing these issues.

Enabling CORS in Django

Since Django is a web framework, it’s very simple to enable CORS. So, here are the steps you must take to do so.

Install the CORS module:

python -m pip install django-cors-headers

Once that’s done, enable the module in Django. This is done in the installed apps section. Oh, and don’t forget the trailing comma; otherwise, you’ll get an error.

Django
INSTALLED_APPS = [
...
'corsheaders',
...
]

Next, add the middleware classes to listen in on server responses. Middleware classes hook on Django’s request/response processing. You can think of it as a plugin system to modify Django’s input or output.

Django
MIDDLEWARE = [
...,
'corsheaders.middleware.CorsMiddleware',
'django.middleware.common.CommonMiddleware',
...,
]

I’d recommend that you place the class CorsMiddleware before any other middleware that can generate responses, such as CommonMiddleware. This is because any other class may prevent the module from generating the appropriate CORS headers.

Finally, configure at least one of the required settings and any of the optional settings that you’d like to. Let’s review those settings and the purpose of each in the next sections.

Required Settings

Required settings tell the module how to evaluate a request’s origin. From there, the module decides, based on the settings you defined, if the origin is valid in order to continue processing the request and to provide a response.

You can set the module to allow requests from specific domains, regular expressions, or all requests. What options you should configure will depend on your back end’s purpose. Sometimes all origins are valid, but in other cases, you’ll need to narrow them to only a few, as shown below.

CORS_ALLOWED_ORIGINS

CORS_ALLOWED_ORIGINS is the list of origins authorized to make requests. For example, below I’ve specified four origins:

Django
CORS_ALLOWED_ORIGINS = [
"https://domain.com",
"https://api.domain.com",
"http://localhost:8080",
"http://127.0.0.1:9000"
]

CORS_ALLOWED_ORIGIN_REGEXES

CORS_ALLOWED_ORIGIN_REGEXES are regular expressions that match domains that can make requests. This setting is especially useful if you have many domains.

Django
CORS_ALLOWED_ORIGIN_REGEXES = [
r"^https://\w+\.domain\.com$",
]

CORS_ALLOW_ALL_ORIGINS

The CORS_ALLOW_ALL_ORIGINS setting accepts only true or false. If true, the server will accept all requests. However, for security purposes, it’s better to use one of the above settings to limit valid request sources.

Optional Parameters

The optional parameters already have default values, which are valid in most situations. But if you need additional fine-grained permissions, these settings are the way to go. With them, you can restrict CORS responses according to URLs. Also, you can allow specific actions (GET, POST, PUT, etc.), specific headers for requests, or even cookies. Let’s review the parameters.

CORS_URLS_REGEX

The CORS_URLS_REGEX setting restricts which URLs the server will send CORS headers to. It’s useful, for example, when you just want to send headers on part of your site. Here’s an example:

CORS_URLS_REGEX = r'^/api/.*$'

CORS_ALLOW_METHODS

The CORS_ALLOW_METHODS setting limits what methods are allowed for CORS. These are the default values:

Django
CORS_ALLOW_METHODS = [
'DELETE',
'GET',
'OPTIONS',
'PATCH',
'POST',
'PUT',
]

CORS_ALLOW_HEADERS

CORS_ALLOW_HEADERS is a list of non-standard headers allowed in the request. The default value is below:

Django
CORS_ALLOW_HEADERS = [
'accept',
'accept-encoding',
'authorization',
'content-type',
'dnt',
'origin',
'user-agent',
'x-csrftoken',
'x-requested-with',
]

CORS_EXPOSE_HEADERS

CORS_EXPOSE_HEADERS is a list of headers exposed to the browser. The default is an empty array.

CORS_PREFLIGHT_MAX_AGE

The CORS_PREFLIGHT_MAX_AGE setting defines the time in seconds a browser can cache a header response to a preflight request. It defaults to 86,400 seconds (one day).

CORS_ALLOW_CREDENTIALS

CORS_ALLOW_CREDENTIALS is a true or false value. So, its value determines whether the server allows cookies in the cross-site HTTP requests.

Watch a demo to see StackHawk in flight

Final Thoughts

As you’ve seen in this post, CORS is a security feature designed to protect the user from malicious websites. In this case, the protection is to allow only specific domains to perform CORS requests. Thus, back-end servers require the proper configuration to accept such requests.

Nevertheless, this feature sometimes can get in the way during your project’s development process. To configure a development environment, you need to consider the security restrictions CORS requires. That makes it a bit tricky. But once you configure it correctly, you can forget all about it.

Still, don’t forget to disable it if all your requests will originate from the same domain once you deploy your app in production. Otherwise, make sure you configure it properly to avoid unexpected errors. As I explained above, for Django, this step is very easy to perform.

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  |  April 30, 2021

Read More

Add AppSec to Your CircleCI Pipeline With the StackHawk Orb

Add AppSec to Your CircleCI Pipeline With the StackHawk Orb

Application Security is Broken. Here is How We Intend to Fix It.

Application Security is Broken. Here is How We Intend to Fix It.

Using StackHawk in GitLab Know Before You Go (Live)

Using StackHawk in GitLab Know Before You Go (Live)