Django security tips
March 25, 2020
0 mins readLucky you, user of the web framework for perfectionists with deadlines (AKA Django)! The Django team has put a lot of thought into their security practice (find security features in their documentation and their security policy is interesting too). We have summarized some of the best tips for keeping your Django project secure.
Download our Django security tips cheat sheet.
Know your version and use a secure one
What version of Django are you using? Django currently has three major versions in use (though long term support for 1.11 is ending in April 2020). The version of Django you use has a number of implications on your project. For instance, Django 1.11 is the last version to use Python 2 (which was sunsetted at the beginning of 2020).
Additionally, the choice of version determines what known vulnerabilities are present and potentially exploitable in your application. Learn more about known Django vulnerabilities from our vulnerability database or scan your project with Snyk, and we will let you know if you are using a vulnerable version.
Finally, it is important that you have a plan in place to keep your Django version up to date. Generally it is a good idea to settle on a long term supported version and have a plan for moving to the next one up before support ends for your current version. That way you benefit from maintainer support, but you don’t have to be changing your version constantly.
As a side note, be sure that you are referencing documentation from the correct Django version. You would not want to believe a security policy is in place because you read about it in the documentation, only to discover that there was a mismatch between the version you are using and the documentation you are reading.
Throttle user authentications
Django provides a lot of security features baked in, but the authentication system does not inherently protect against brute force attacks. A malicious actor could hit your system with numerous login attempts, and potentially get in.
If this kind of attack is of concern for your project, use a project like Django Defender to lock out users after too many login attempts.
Protect your source code
Protecting your source code may seem to be an obvious step, but it is a multi-faceted step and is, therefore, worth exploring. One way to protect your source code is to make sure that it is not included in your web server’s root directory. If it is, there is a possibility that it is served or that, part of it, is executed in a way that you had not planned.
And although it goes without saying, if your project is sensitive, be sure to use a private repository on GitHub, Bitbucket, or Gitlab. Also, make sure to never check your secrets into your version control system, regardless of whether you intend to use a private repo. It is possible that a private repository does not always stay private and someone with access to a private repo cannot always be trusted.
Use raw queries and custom SQL with caution
While it is tempting to write raw sql queries and custom SQL, doing so may open the door for an attack. Django’s object-relational-mapping (ORM) framework is designed to make querying your database easy. Querysets are constructed using query paramatization. The queries parameters have been abstracted away from the queries sql code. A user attempting to perform a sql injection (execute arbitrary sql on a database) is going to find it much harder if you always use the ORM.
Django does allow the use of raw queries, but their use is not recommended. If you do use them, take extra care to properly escape any parameters. If you find the Django ORM to be insufficient for your needs, it is possible to use a different ORM within Django. SQLAlchemy is an example of an ORM that can be used with Django. If there is an ORM that better suits your project, making use of it is preferable to writing large amounts of raw sql.
Use HTTPS
Regardless of your framework of choice, it is always preferable to deploy behind HTTPS. Doing so prevents malicious users from intercepting information sent between the client and the server. For https to work correctly, it is important to be sure that you have properly determined your settings.
The following settings are important to your use of https. Be sure you understand them and enable them properly. The documentation does a good job of explaining the settings, the secure defaults, and why you might need to change them in a given circumstance.
Watch your headers
First, we want to specifically consider the referer request header. This header contains the address of the previous web page from which you arrived at the current page. This information is useful for analytics, among other things. Sometimes though, It causes problems because, generally, people do not like to be followed around the web. Due to privacy concerns, it is possible to disable this functionality. Whether a referer request header is passed along, can be determined by the referer-policy header. Under certain conditions, partial information is passed along — in others, all of the information is included, and sometimes no referer request header information is forwarded.
When the site is served via https, the referer request header is utilized by Django to help prevent cross site request forgery (CRSF) attacks. If you are too strict with your referer-policy header, you disable the functionality of Django’s CRSF protection. In the end, you need to weigh the privacy benefits of using a strict referer-policy header with the benefits of the CRSF protection. It is possible to “split the difference” by only enabling same-origin referrers in your referer-policy header.
Be careful with your cookies
Some cookies are more secure than others — the default cookie behavior is to connect over http. However, since we already established that you need to use https, you want to make sure your cookies are only being sent over https as well. To prevent leaking cookies, be sure to set your SESSION_COOKIE_SECURE and CSRF_COOKIE_SECURE settings to True. This instructs the browser to only send these cookies over HTTPS connections. There are some interesting side effects to setting these parameters to true, but they should be mitigated by redirecting http traffic to https.
Carefully handle user uploads
If your web application allows users to upload files, you are opening yourself to an attack vector and the upload logic should therefore be handled carefully. First, it is important to validate all uploaded files to be sure they are what you expect (for instance, an image file and not a PHP script!). You don’t want it to be possible for a user to run code.
There are a number of things to help validate uploaded files or handle them more carefully. They include the following:
Create a whitelist of acceptable file types
Limit file size allowed for uploads to prevent denial of service problems
Disable handlers that would execute static files as code (for example, disable Apache’s mod_php)
Make cross site scripting protections work for you. If you serve user content from a distinct top level domain, the protections against cross site scripting will kick in to protect you. This can be a domain that you manage, although it is probably easier to serve the files from a cloud service or content delivery network provider.
Understand all of your dependencies
If you have selected a Django version with little to no known security risks, you probably think that there are no more security issues to consider. However, it is important to understand the open source libraries that Django is importing and whether there are vulnerabilities in those libraries.
When you include Django (or any open source library for that matter), typically, you are _also_including every library that your library of choice utilizes. Open source is built on open source which is built on more open source.
Indirect dependencies are as likely to introduce risk as direct dependencies, but these risks are less likely to be recognized. A tool like Snyk helps you understand your entire dependency tree, and now that Snyk offers fix pull requests for Python, fixing problems (even in indirect dependencies) is easier than ever.
Don’t let the perfect get in the way of the good
Every security step you take is a step in the right direction. Django may be for perfectionists with deadlines, but code doesn’t have to be perfect to reap security benefits. Implementing the concepts discussed above, to the best of your ability, can dramatically improve the security of your code and result in a healthier, more resilient project. Happy coding, Pythonistas!
Get started in capture the flag
Learn how to solve capture the flag challenges by watching our virtual 101 workshop on demand.