This article is the fourth in a series exploring how to effectively utilize pydantic in Django projects. As a quick recap: The first article demonstrated how pydantic’s type hinting simplifies Django settings configuration. The second article guided readers through building a web app with Docker, using these principles to maintain consistency between development and production environments. In the third article, we deployed this app to Heroku.
Django, designed with a security-first approach, stands apart from libraries like Flask and FastAPI by providing built-in safeguards against common vulnerabilities. In this article, we will enhance the security of a functional web application, already deployed and accessible online, using Django’s built-in features.
To follow along, ensure you have deployed the example web application from the first article in this series. We’ll then assess, strengthen, and verify the Django app’s security, resulting in a site that strictly enforces HTTPS.
Step 1: Assessing Application Security
Navigating to the application’s root directory and executing the following command initiates Django’s security check and site verification process:
| |
However, this command is already integrated into our app’s heroku-release.sh file (as part of the steps outlined in part 3 of this series) and is automatically executed upon application deployment.
The check command in the script generates a series of Django security warnings](https://docs.djangoproject.com/en/4.0/ref/checks/), accessible by clicking the Show Release Log button in [Heroku’s dashboard. For our application, the output is:
| |
This output highlights four security areas that require attention:
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 systematically address each of these four identified concerns. Importantly, our HSTS implementation will take into account the (security.W004) warning regarding potential site breakage from careless HSTS activation.
Step 2: Enhancing Django Application Security
Before we can implement security measures related to HTTPS (HTTP with SSL), we first need to enable HTTPS by configuring our web app to accept SSL requests.
To do so, we’ll set up the USE_SSL configuration variable. While this variable won’t immediately alter the app’s behavior, it’s a crucial first step towards further configuration adjustments.
In the Heroku dashboard, navigate to the Config Vars section under the Settings tab to view the configured key-value pairs:
Key | Value |
|---|---|
ALLOWED_HOSTS | ["hello-visitor.herokuapp.com"] |
SECRET_KEY | Use the generated key value |
DEBUG | False |
DEBUG_TEMPLATES | False |
Django security settings are conventionally stored within a web app’s settings.py file. The settings.py file contains the SettingsFromEnvironment class responsible for handling environment variables. We’ll add a new configuration variable named USE_SSL and set its value to TRUE, which SettingsFromEnvironment will process.
While we are in the settings.py file, let’s also update the existing variables for HTTPS, session cookies, and CSRF cookies. We will enable HSTS later, as this requires an additional step.
Here are the key edits to support SSL and update the three existing variables:
| |
These Django security updates are crucial for protecting our application. Each Django setting is labeled with its corresponding security warning identifier in a code comment.
The SECURE_PROXY_SSL_HEADER and SECURE_SSL_REDIRECT settings guarantee that our application can only be accessed via HTTPS, which is significantly more secure than unencrypted HTTP. Our modifications will ensure that any browser attempting to connect to our site using HTTP is redirected to use HTTPS.
To enable HTTPS, we need to obtain an SSL certificate. Fortunately, Heroku’s Automated Certificate Management (ACM) feature provides this functionality and is enabled by default for Basic or Professional dynos.
After adding these settings to the settings.py file, we can push the changes, go to the Heroku admin panel, and trigger another application deployment from the repository to apply these changes to our live site.
Step 3: Verifying HTTPS Redirection
Once deployment is complete, we need to verify that the HTTPS functionalities are working as expected by confirming that the site:
- Is directly accessible using the
https://prefix. - Redirects from HTTP to HTTPS when accessed using the
http://prefix.
Successfully implementing HTTPS redirection addresses three out of the four initial warnings (numbers 2, 3, and 4). The remaining concern is HSTS.
Step 4: Implementing HSTS Policy
HTTP Strict Transport Security (HSTS) is a security mechanism that forces compatible browsers to connect to our site exclusively over HTTPS. Upon the very first HTTPS access to our site from a compatible browser, HSTS sends a Strict-Transport-Security header response, which then prevents any future HTTP access.
Unlike standard, page-specific HTTPS redirection, HSTS redirection applies to the entire domain. This means that, without HSTS, a site with a thousand pages could potentially require a thousand individual HTTPS redirection requests.
Furthermore, HSTS utilizes its own dedicated cache, which persists even when a user clears their regular browser cache.
To implement HSTS support, we’ll make the following updates to our app’s settings.py file:
| |
Next, scroll to the bottom of the else block immediately following the code above and add these lines:
| |
We have now enabled HSTS by updating three settings, as per the recommendations of Django documentation, and opted to submit our site to the browser preload list. Recall that the (security.W004) warning cautioned against prematurely enabling HSTS. To mitigate any potential issues, we set SECURE_HSTS_SECONDS to one hour. This represents the duration for which your site would be inaccessible if HSTS is set up incorrectly. By testing with a shorter timeframe, we can confirm the server configuration is compatible before increasing it. A common setting is 31536000 seconds, which equates to one year.
With all four security steps implemented, our site now benefits from both HTTPS redirection logic and an HSTS header, ensuring that all connections are secured with SSL.
Another advantage of structuring our settings logic around the USE_SSL configuration variable is that a single code instance (the settings.py file) functions seamlessly across both development and production environments.
Django Security for Peace of Mind
While website security is a complex undertaking, Django simplifies the process with a few straightforward yet essential steps. Whether you are a seasoned security expert or a relative newcomer, the Django platform empowers you to protect your sites with relative ease. I have successfully deployed countless Django applications to Heroku, allowing both myself and my clients to rest easy knowing their applications are secure.
The Toptal Engineering Blog would like to express its sincere gratitude to Stephen Harris Davidson for reviewing and beta testing the code samples provided in this article.