In this article, we briefly cover SQL injection attacks and how they relate to Django applications. We will define the basic idea of a SQL injection attack, cover the most common attack vectors in a Django application, provide a simple example and offer suggestions on ways to combat these types of attacks.
As the number one item on the Open Web Application Security Project (OWASP) top ten list of web security risks, SQL injection is one of the most common attacks used in modern web applications. SQL injection attacks are something that every web developer should understand and guard against in their applications.
What is a SQL Injection Attack?
Structured Query Language (SQL) injection attacks are a form of attack against data-driven software applications that take advantage of vulnerabilities in the handling and processing of user input (for example), that results in the execution of malicious SQL code intended to provide unauthorized access to view, modify, delete, or otherwise leak or dump relational data.
Simply put, a SQL injection attack exposes an application’s data to unauthorized use and possible modification. A successful attack of this nature against an organization’s web application(s) can cost dearly.
How would a Django system most likely be vulnerable?
Django RawSQL queries, the .raw Manager method, the connection cursor, and the QuerySet .extra method are the four main ways to infiltrate a Django application with SQL injection. Since these methods of executing queries avoid the Django Object Relational Mapper (ORM), they do not benefit from the automatic query parameterization that it provides. This means that the SQL queries written using these features of Django can be used as an attack vector when applied incorrectly or haphazardly.
The official Django security documentation goes over these features in-depth here.
An Example of SQL Injection in Django
The following example illustrates how a Django application can be vulnerable to a SQL injection attack:
What are some ways to avoid SQL Injection Attacks?
The most common way to protect against a SQL injection attack in Django is to always use the ORM and avoid writing raw SQL code as much as possible (avoid the Django raw SQL features mentioned above). The Django ORM provides automatic query parameterization by separating the SQL query from user input so that any maliciously provided SQL is properly escaped and handled.
If you do write raw SQL code, for instance, to avoid some awkward ORM syntax or to optimize an otherwise particularly slow query, make sure that you heavily scrutinize it through peer code reviews, unit tests, and even targeted penetration testing.
In the example above, a simple solution is to pass the user-provided first_name value to Django to properly escape it for us:
SQL injection is a common and truly insidious form of attack due to it being incredibly easy to miss when writing otherwise innocuous code. String interpolation of user data is a common technique in Python programming, but, as we have illustrated, can lead to some very disastrous consequences when handed off to a database system.
The main take away for Django users is to limit the use of raw SQL features entirely, and, in the cases where they are absolutely needed for other concerns, to make sure to take full advantage of what Django provides for avoiding these attacks, such as using the params keyword argument in our example above.