Security in Django Applications: A Pydantic Tutorial, Part 4
Django’s built-in security checker identifies vulnerabilities with ease. Here’s how you can enforce HTTPS redirects to tighten security on your web applications.
Django’s built-in security checker identifies vulnerabilities with ease. Here’s how you can enforce HTTPS redirects to tighten security on your web applications.
Arjaan is a software engineer and data scientist who creates mission-critical, Python-based cloud solutions focused on Rasa for international banks and insurance companies. He also architects and teaches large-scale Kubernetes solutions. Arjaan founded a company for the resale of manufacturing simulation software that he co-developed.
Previous Role
FounderPREVIOUSLY AT
This is the fourth installment in a series on leveraging pydantic for Django-based projects. Before we continue, let’s review: In the series’ first installment, we focused on pydantic’s use of Python type hints to streamline Django settings management. In the second tutorial, we used Docker while building a web application based on this concept, aligning our development and production environments. The third article described hosting our app on Heroku.
Written with a security-first design principle—a departure from Python libraries such as Flask and FastAPI—Django features baked-in support for identifying many common security pitfalls. Using a functional web application example, running and available to the internet, we will leverage Django to enhance application security.
To follow along, please be sure to first deploy our example web application, as described in the first installment of this tutorial series. We will then assess, fortify, and verify our Django app’s security, resulting in a site that strictly supports HTTPS.
Step 1: Evaluate Application Vulnerabilities
One way to perform Django’s security check and site verification sequence is to navigate to our application’s root directory and run:
python manage.py check --deploy --fail-level WARNING
But this command is already contained in our app’s heroku-release.sh
file (per the steps taken in part 3 of this tutorial series), and the script automatically runs when the application is deployed.
The check
command in the preceding script generates a list of Django security-related warnings, viewable by clicking the Show Release Log button in Heroku’s dashboard. The output for our application is as follows:
System check identified some issues:
WARNINGS:
?: (security.W004) You have not set a value for the SECURE_HSTS_SECONDS setting. If your entire site is served only over SSL, you may want to consider setting a value and enabling HTTP Strict Transport Security. Be sure to read the documentation first; enabling HSTS carelessly can cause serious, irreversible problems.
?: (security.W008) Your SECURE_SSL_REDIRECT setting is not set to True. Unless your site should be available over both SSL and non-SSL connections, you may want to either set this setting True or configure a load balancer or reverse-proxy server to redirect all connections to HTTPS.
?: (security.W012) SESSION_COOKIE_SECURE is not set to True. Using a secure-only session cookie makes it more difficult for network traffic sniffers to hijack user sessions.
?: (security.W016) You have 'django.middleware.csrf.CsrfViewMiddleware' in your MIDDLEWARE, but you have not set CSRF_COOKIE_SECURE to True. Using a secure-only CSRF cookie makes it more difficult for network traffic sniffers to steal the CSRF token.
System check identified 4 issues (0 silenced).
Reinterpreted, the preceding list suggests we address the following four security concerns:
Item | Value (Requirement: Set to | Outcome |
---|---|---|
HSTS |
| Enables HTTP Strict Transport Security. |
HTTPS |
| Redirects all connections to HTTPS. |
Session Cookie |
| Impedes user session hijacking. |
CSRF Cookie |
| Hinders theft of the CSRF token. |
We will now address each of the four issues identified. Our HSTS setup will account for the (security.W004)
warning’s message about enabling HSTS carelessly to avoid major site breakage.
Step 2: Bolster Django Application Security
Before we address security concerns pertaining to HTTPS, a version of HTTP that uses the SSL protocol, we must first enable HTTPS by configuring our web app to accept SSL requests.
To support SSL requests, we will set up the configuration variable USE_SSL
. Setting up this variable will not change our app’s behavior, but it is the first step toward additional configuration modifications.
Let’s navigate to the Heroku dashboard’s Config Vars section of the Settings tab, where we can view our configured key-value pairs:
Key | Value |
---|---|
ALLOWED_HOSTS | ["hello-visitor.herokuapp.com"] |
SECRET_KEY | Use the generated key value |
DEBUG | False |
DEBUG_TEMPLATES | False |
By convention, Django security settings are stored within a web app’s settings.py
file. settings.py
includes the SettingsFromEnvironment
class that is responsible for environment variables. Let’s add a new configuration variable, setting its key to USE_SSL
and its value to TRUE
. SettingsFromEnvironment
will respond and handle this variable.
While in our settings.py
file, let’s also update the HTTPS, session cookie, and CSRF cookie variable values. We will wait to enable HSTS, as this requires an additional step.
The key edits to support SSL and update these three existing variables are:
class SettingsFromEnvironment(BaseSettings):
USE_SSL: bool = False
try:
# ...
USE_SSL = config.USE_SSL
# ...
if not USE_SSL:
SECURE_PROXY_SSL_HEADER = None
SECURE_SSL_REDIRECT = False
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SECURE = False
else:
# (security.W008)
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SECURE_SSL_REDIRECT = True
# (security.W012)
SESSION_COOKIE_SECURE = True
# (security.W016)
CSRF_COOKIE_SECURE = True
These Django security updates are important for the protection of our application. Each Django setting is labeled with its corresponding security warning identifier as a code comment.
The SECURE_PROXY_SSL_HEADER
and SECURE_SSL_REDIRECT
settings ensure our application only supports connection to our site via HTTPS, a far more secure option than unencrypted HTTP. Our modifications will ensure that a browser trying to connect to our site via HTTP is redirected to connect via HTTPS.
To support HTTPS, we need to provide an SSL certificate. Heroku’s Automated Certificate Management (ACM) feature fits the bill, and is set up by default for Basic or Professional dynos.
With these settings added to the settings.py
file, we can push our code changes, navigate to Heroku’s admin panel, and trigger another application deployment from the repo to manifest these changes on our site.
Step 3: Verify HTTPS Redirection
After deployment completes, let’s check the HTTPS functionalities on our site and confirm that the site:
- Is directly accessible using the
https://
prefix. - Redirects from HTTP to HTTPS when using the
http://
prefix.
With HTTPS redirection working, we have addressed three of our four initial warnings (nos. 2, 3, and 4). Our remaining concern to address is HSTS.
Step 4: Enforce HSTS Policy
HTTP Strict Transport Security (HSTS) restricts compatible browsers to only using HTTPS to connect to our site. The very first time our site is accessed via a compatible browser and over HTTPS, HSTS will return a Strict-Transport-Security
header response that prevents HTTP access from that point forward.
In contrast with standard HTTPS redirection that is page-specific, HSTS redirection applies to an entire domain. In other words, without HSTS support, a thousand-page website could potentially be burdened with a thousand unique requests for HTTPS redirection.
Additionally, HSTS uses its own, separate cache that will remain intact, even when a user clears their “regular” cache.
To implement HSTS support, let’s update our app’s settings.py
file:
if not USE_SSL:
SECURE_PROXY_SSL_HEADER = None
SECURE_SSL_REDIRECT = False
SESSION_COOKIE_SECURE = False
CSRF_COOKIE_SECURE = False
+ SECURE_HSTS_INCLUDE_SUBDOMAINS = False
+ SECURE_HSTS_PRELOAD = False
Then skip down to the bottom of the else
block just after that and add these lines:
# IMPORTANT:
# (-) Add these only once the HTTPS redirect is confirmed to work
#
# (security.W004)
SECURE_HSTS_SECONDS = 3600 # 1 hour
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_HSTS_PRELOAD = True
We have updated three settings to enable HSTS, as recommended by Django documentation, and chosen to submit our site to the browser preload list. You may recall that our (security.W004)
warned against carelessly enabling HSTS. To avoid any mishaps related to prematurely enabled HSTS, we set the value for SECURE_HSTS_SECONDS
to one hour; this is the amount of time your site would be broken if set up improperly. We will test HSTS with this smaller value to confirm that the server configuration is compatible before we increase it—a common option is 31536000
seconds, or one year.
Now that we have implemented all four security steps, our site is armed with HTTPS redirect logic combined with an HSTS header, thus ensuring that connections are supported by the added security of SSL.
An added benefit of coding our settings logic around the USE_SSL
configuration variable is that a single instance of code (the settings.py
file) works on both our development system and our production servers.
Django Security for Peace of Mind
Safeguarding a site is no easy feat, but Django makes it possible with a few simple, yet crucial, steps. The Django development platform empowers you to protect a site with relative ease, irrespective of whether you are a security expert or a novice. I have successfully deployed countless Django applications to Heroku and I sleep well at night—as do my clients.
The Toptal Engineering Blog extends its gratitude to Stephen Harris Davidson for reviewing and beta testing the code samples presented in this article.
Further Reading on the Toptal Blog:
Understanding the basics
How do I secure my Django app?
Two significant ingredients are needed to secure a Django application: the correct use and configuration of an SSL certificate and support for HTTP Strict Transport Security (HSTS).
Are Django apps secure?
Yes, when a Django app’s website and hosting environment are configured correctly it is secure. Django is built with foundational security principles by design.
Which Django application is used to provide security?
Django provides a built-in security checker that highlights security vulnerabilities it detects within the application.
What are the disadvantages of Django?
As with other internet-available and hosted applications, Django is susceptible to attacks from malicious third parties if its security is not carefully handled.
What are some of the most common vulnerabilities that Django applications are susceptible to?
The incorrect application of SSL certificates and HSTS misconfiguration are two common issues that may lead to a site’s data and functionality becoming vulnerable to outside attackers.
Plymouth, MI, United States
Member since June 4, 2018
About the author
Arjaan is a software engineer and data scientist who creates mission-critical, Python-based cloud solutions focused on Rasa for international banks and insurance companies. He also architects and teaches large-scale Kubernetes solutions. Arjaan founded a company for the resale of manufacturing simulation software that he co-developed.
Previous Role
FounderPREVIOUSLY AT