Vulnerability
Django

SQL Injection in Django Applications

Django's ORM is one of the safest database abstraction layers available, automatically parameterizing all queries. However, raw(), extra(), RawSQL(), and cursor.execute() with string formatting bypass this protection and are common sources of SQL injection in Django applications.

Scan Your Django App

How SQL Injection Manifests in Django

Django's ORM (Model.objects.filter()) is safe by default. SQL injection occurs when developers use: - raw() with string formatting: User.objects.raw(f"SELECT * FROM users WHERE name = '{name}'") - extra() with unparameterized where clauses - RawSQL() expressions with string concatenation - cursor.execute() with f-strings or % formatting Dynamic ORDER BY and custom database functions that interpolate user input are also common vectors. The Django documentation warns about these patterns, but developers under time pressure frequently take shortcuts.

Real-World Impact

A Django REST API used raw() to build a complex reporting query with user-supplied date ranges. The developer used f-string formatting instead of parameter binding. An attacker exploited this to extract the entire database schema and dump sensitive data including payment information, resulting in a data breach notification to 200,000 users.

Step-by-Step Fix

1

Use parameterized raw queries

Always pass user input as parameters, never format them into the SQL string.

# UNSAFE - string formatting
User.objects.raw(f"SELECT * FROM auth_user WHERE email = '{email}'")
User.objects.raw("SELECT * FROM auth_user WHERE email = '%s'" % email)

# SAFE - parameterized
User.objects.raw("SELECT * FROM auth_user WHERE email = %s", [email])
2

Replace extra() with ORM expressions

The extra() method is deprecated and unsafe. Use Django ORM features instead.

# UNSAFE - extra() with string formatting
User.objects.extra(where=[f"age > {min_age}"])

# SAFE - ORM filter
from django.db.models import F, Q
User.objects.filter(age__gt=min_age)

# SAFE - for complex queries, use annotations
from django.db.models import Subquery, OuterRef
User.objects.annotate(
    last_order=Subquery(
        Order.objects.filter(user=OuterRef('pk')).values('date')[:1]
    )
)
3

Secure cursor.execute() calls

Use Django's parameter substitution for direct database cursor queries.

from django.db import connection

# UNSAFE
with connection.cursor() as cursor:
    cursor.execute(f"SELECT * FROM products WHERE category = '{category}'")

# SAFE
with connection.cursor() as cursor:
    cursor.execute("SELECT * FROM products WHERE category = %s", [category])
4

Allowlist dynamic ORDER BY

Validate sort fields against a known allowlist.

ALLOWED_SORT_FIELDS = {'name', 'created_at', 'price', '-name', '-created_at', '-price'}

def product_list(request):
    sort = request.GET.get('sort', 'created_at')
    if sort not in ALLOWED_SORT_FIELDS:
        sort = 'created_at'
    return Product.objects.all().order_by(sort)

Prevention Best Practices

1. Use Django ORM methods (filter, exclude, annotate) which are automatically parameterized. 2. When using raw(), always pass parameters as a list, never use string formatting. 3. Avoid extra() entirely - it is deprecated. Use Subquery, OuterRef, and annotations instead. 4. For cursor.execute(), always use %s placeholders with a params tuple. 5. Validate and allowlist any dynamic column names or sort orders.

How to Test

1. Search for .raw(, .extra(, RawSQL(, and cursor.execute( in your codebase. 2. Check if any use string formatting (f-strings, .format(), %) instead of parameter lists. 3. Test API endpoints with ' OR 1=1 -- in query parameters. 4. Use Django Debug Toolbar to inspect actual SQL queries. 5. Use Vibe App Scanner to detect SQL injection patterns in your Django application.

Frequently Asked Questions

Is the Django ORM safe from SQL injection?

Yes, Django ORM methods like filter(), exclude(), get(), and create() automatically parameterize all queries. SQL injection only occurs when you bypass the ORM using raw(), extra(), RawSQL(), or cursor.execute() with string formatting.

Can Django model field lookups be exploited?

Standard field lookups (field__contains, field__startswith) are safe. However, if you dynamically construct lookup keys from user input (like using **{user_input: value}), an attacker could inject unexpected lookups. Always validate lookup field names.

Is extra() deprecated in Django?

Yes, extra() is effectively deprecated. Django recommends using ORM expressions (F(), Q(), Subquery, OuterRef, annotations) instead. extra() is harder to use safely because its where clauses do not support automatic parameterization in all cases.

Is Your Django App Vulnerable to SQL Injection?

VAS automatically scans for sql injection vulnerabilities in Django applications and provides step-by-step remediation guidance with code examples.

Scans from $5, results in minutes. Get actionable fixes tailored to your Django stack.