This check looks for ways that Django SQL Injection protection is being bypassed, by using quoted parameters.

The check looks at the following use cases:

  • Use of RawSQL() constructor directly
  • Use of cursor.execute()
  • Use of raw() on a Manager instance

Whilst the methods support parametrized queries, if the %s value is quoted with single-quotes, the value is still vulnerable to SQL injection.

This also applies to parametrized arguments, where it includes a substitute reference, e.g. '%(variable)s'.


The first example is using the RawSQL constructor directly and annotating a query set:

from django.db.models.expressions import RawSQL

qs.annotate(val=RawSQL("select col from sometable where othercol = '%s'", (someparam,)))  # this is bad!

Another example is using the raw() method on a manager to filter results, exposing SQL injection:

from django import things
from .models import User

def my_view(self):
    User.objects.raw("SELECT * FROM myapp_person WHERE last_name = '%s'", [lname])  # this is also bad!

Cursors can also be exploited using the same technique:

from django.db import connection

def my_custom_sql(self):
    with connection.cursor() as cursor:
        cursor.execute("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz])
        cursor.execute("SELECT foo FROM bar WHERE baz = '%s'", [self.baz])
        row = cursor.fetchone()
    return row


Remove the quotations from the string values:

("UPDATE bar SET foo = 1 WHERE baz = %s", [self.baz]) # good
("UPDATE bar SET foo = 1 WHERE baz = '%s'", [self.baz]) # bad!