General

Django Security and SQL Protections

A draft for a blog post on Django’s security measures 

Previously written as a blog post for a Web Application Sec

Django is an open source web framework that labels itself as “The web framework for perfectionists with deadlines.” A very fitting title with my 4 almost 5 years of experience using it. Used by big names like Mozzila, EventBrite, The Washington Post, Dropbox, BitBucket, Spotify, Django is no stranger to the web industry. Written in Python it shares some similarities to Flask, however Flask is a minimalistic approach whereas Django is shipped with an “everything you could possibly need” approach. In doing so Django includes authentication, wide database support, template support, and its amazing Object Relational Manager (ORM) that I’ll get into detail later. This post makes references and is based off the Django project GitHub page available at GitHub.com/Django/Django which is the master branch, or version 3. My experiences with Django go back to 1.12 and lie mostly within Django 2.2 nowadays as we slowly move things to Django 3. Most notably Django 3 deals with the adaptation to ASGI instead of the commonly used WSGI, ASGI within Django is known as channels and allows for socket based real time connections between the client and server.

But first, let’s briefly understand how a Django project works, to better understand how it functions. A given Django project is comprised of apps, each app can have any purpose although an app is usually designated to a specific functionality. Within an app are routes, views, models, and forms. Routes are simply the routes that match a path like /about to a view like AboutPage. Views handle the core of the runtime logic that are executed when a route is visited, computing any logic ultimately to then return a template that is rendered to the user. Computing any logic will oftentimes include manipulating the database and stored information utilizing the Django ORM. To do so the database must know what tables and columns to setup to store data, models do exactly this. Specifying a model class will create a table, where fields are then defined to a wide array of datatypes. Datatypes like Foreign Keys (FKs) and ManyToMany fields (MTM) highlight the great power that the ORM has, allowing that field to save instances of other models.

To speak to the ORM directly it allows for instances to be created, updated, modified, or deleted, which sounds ordinary but the ease yet complexity it offers makes it very powerful when writing applications. Most commonly I find myself using MTM and FK fields to associate objects with each other, something that’s done with such ease and opens the door to endless possibilities.

SQL Injection quite simply refers to the injection of SQL commands via input forms, and in doing so an attacker may be able to gain access to data or information that is not readily made available. In some cases an attacker may even be able to access data on other users.

For combatting SQL Injection, OWASP recommends[1] parameterized statements, use of stored procedures, whitelist input validation, and escaping all user supplied input.

Parameterized statements are simply prepared statements with variable bonding. It requires the developer to fire define all the SQL code for statements that are executed. The OWASP resource contains many specifics for how it can be implemented within your language of choice. It’s how developers should write SQL statements, however everyone in the security field can attest to the strong laziness trait almost all developers have.

Stored Procedures are not always safe from SQL injection quite simply put.

The difference between prepared statements and stored procedures is that the SQL code for a stored procedure is defined and stored in the database itself, and then called from the application. Both play a role in the effectiveness of preventing SQL injection.

For additional defenses OWASP suggests enforcing least privilege, and performing whitelist input validation as a secondary defense.

For Django, like recommendations made public by OWASP, this is accomplished via sanitized inputs, as well as parameterized statements. Out of the box Django supports multiple different database backends, ranging from sqlite3 for local testing purposes, as well as PostgreSQL, oracle, others. You can find this at Django/Django/db/backends on the Django project GitHub page. Django contains many different database drivers that allow for a specific and point to point proper implementations with that specific database.

Let’s look at a parameterized statement in depth, so we can better understand how it works and what it looks like.

The above picture shows parameterized statements for schema-based operations, or operations dealing with the modification of the database schema/structure. Click here to see the code on GitHub (or find it in Django/Django/db/backends/base/schema.py. Picking one line out will show the SQL statement with 2 arguments it takes in, this being what the application needs to call to.

Built into Django’s own database drivers for each of these databases are parameterized statements in the specified command format.

A database driver, for instance SQLite provides the specifics on how to properly integrate with that database. An example of why can be found here. SQLite as well as other databases don’t support the datatypes that Django supports. And In doing so they need to be mapped appropriately. The screenshot below shows Django/Django/db/backends/sqlite3/base.py around line 80. Base.py deals with functions and the actual backend for the sqlite3 library.

Remaining within the Django/Django/db/backends/sqlite3/ directory we can see client.py and creation.py where yes it deals with the creation of the .sqlite database file. Features.py deals with specific database features that sqlite has for compatibility within various Django functions. Operations.py similarly deals with functions for executing certain SQL statements, examples within operations.py are specific Django date and time SQL functionality like the image below.

That’s enough SQL, SQL injection is just one part of the picture for web application security, what about CSRF, XSS, Click-jacking, and others?

The short answer is yes, Django has thorough protections in place for each of these. If my sales pitch for you to use this framework hasn’t been enough yet I will walk you through some of the security measures in place for the other common security issues.

Cross Site Request Forgery (CSRF), speaks to allowing a malicious user to execute actions using credentials of another user, without knowledge or consent. Django accomplishes this quite simply with regards to forms with CSRF tokens. It can be utilized as simple as this…

[2]

Within Django’s source code it can be found here – Django/Django/template/context_processors.py

What’s a context processor you must ask? It’s nothing too complicate, and another awesome function of Django. It deals specifically with templates and its template engine. Within a Django template (any ordinary HTML file rendered by Django) can utilize template tags and template logic. {{ variable }} is a simple way to output a variable or object that is passed from the view to the template, a very handy feature with regards to objects, like so {{ pizza.sauce.name }} could get the Pizza objects sauce field which is a foreign key to a type of sauce with a character field called name. But what logic can be done with template tags? There’s a lot, however myself I find myself using

{% for x in list %} {{ x }} {% endfor %} – to print all values x in a list called list. Otherwise {% extends ‘file.html %} and {% includes ‘file.html’ %} allows for other template files to be extended or included within another template. Lastly if statements can be implemented to add other logic, below is an example.

{% if len(pizza.toppings) == 0 %} <p>No toppings!</p>{% endif %}

Now that you understand templates, template tags, and context processors we can look below to Django/Django/template/context_processors.py

The code above shows the usage of CSRF tokens within a context processor and templates. The call to get_token is within Django.middleware.csrf or Django/Django/middleware/csrf.py

The code shown above as well as other methods within csrf.py deal with generation, sanitization, and handling of the CSRF token.

Cross site scripting(XSS) is when a user is able to inject client side scripts into browsers of other users. The simple answer for how this is protected is templates. It’s important to note that Django’s protections cover the majority of XSS attacks but not all. Django’s templates conquer this by escaping specific character that are dangerous to HTML, for instance < is converted to &lt; and > is converted to &gt; something that can help combat input of <script>alert(‘django please’)</script>. This protection can be turned off within templates if for whatever reason you don’t like security.

{% autoescape off %} <p> auto escape is off, why tho??</p> {% endautoescape %}

The screenshot below shows Django/Django/template/defaulttags.py which houses all of the template tags logic, bellowing showing auto escape.

Line 31 of the same file shows the actual actions of the auto escape tag, as seen below

Clickjacking is protected within Django thanks to the X-Frame-Options middleware. Django/Django/middleware/clickjacking.py deals with such protection. An example can be seen below.

Django is such a powerful framework, and to those that love python it’s a walk in the park once you can familiarize yourself with the concepts of the framework. I almost always see no use to Flask in comparison to Django unless you’re writing a simple CDN or some super simple webapp. Django is almost always the way to go, and I’ve yet to find any limiting functionality or reasons to not use it for every webapp project I do. Because it’s python (something I myself forget sometimes) you have all the amazing stuff about python like easy file input and parsing, strings that are actual strings (I’m talking to you C), and errors that actually mean something and don’t just randomly occur(yeah I’m talking to you PHP). I hope that beyond the understanding of the framework you’re now able to grasp the ways web frameworks are protected, should you try and exploit one, or should you try and write one yourself (ensuring its secure of course).

  1. https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html

  2. https://docs.djangoproject.com/en/3.1/ref/csrf/#how-to-use-it

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.