HIGH sql injectiondjangocockroachdb

Sql Injection in Django with Cockroachdb

Sql Injection in Django with Cockroachdb — how this specific combination creates or exposes the vulnerability

SQL injection occurs when untrusted input is concatenated into SQL strings and executed by the database. With CockroachDB, the PostgreSQL-wire compatible protocol means standard Django database parameterization rules apply, but misunderstandings about how Django’s ORM and raw query handling interact with CockroachDB can still lead to injection.

Django’s primary defense is the query parameterization provided by cursor.execute(sql, params) and the ORM’s filter()/exclude() methods. These ensure values are sent separately from SQL syntax, which CockroachDB respects over the PostgreSQL protocol. However, injection becomes possible when developers bypass these protections. Examples include:

  • Using Python string formatting or concatenation with raw SQL, e.g., f"SELECT * FROM accounts WHERE id = {user_id}", or "SELECT * FROM accounts WHERE id = %s" % user_id.
  • Passing unsafe strings to extra(select=...), raw(), or execute_sql() without parameterization.
  • Assuming CockroachDB’s strict typing or escaping routines alone prevent injection, rather than relying on parameterized queries.

Even though CockroachDB implements a PostgreSQL wire protocol and uses a SQL parser aligned with PostgreSQL behavior, it does not perform additional magic to sanitize inputs that are already embedded into the SQL string by the client. If the client sends a string containing injected SQL, CockroachDB will execute it. Therefore, the risk with Django + CockroachDB is not a CockroachDB-specific flaw, but a consequence of writing queries that do not keep data and commands separate.

Common attack patterns include authentication bypass via tautologies (e.g., ' OR 1=1 --) and data exfiltration using union-based or error-based techniques. Because CockroachDB supports standard PostgreSQL syntax, payloads that work against PostgreSQL are also effective when improperly parameterized inputs are passed through Django.

Cockroachdb-Specific Remediation in Django — concrete code fixes

Remediation centers on strict use of parameterized queries and avoiding string interpolation. Below are concrete, CockroachDB-compatible code examples for Django.

Safe Query Patterns

Always use Django’s ORM for filtering and updates. The ORM generates parameterized SQL under the hood, which works correctly with CockroachDB.

# Safe: using filter with keyword arguments
users = Account.objects.filter(email=email, status='active')

# Safe: using Q objects for complex conditions
from django.db.models import Q
users = Account.objects.filter(Q(email=email) | Q(username=username))

# Safe: using get with parameters
try:
    account = Account.objects.get(account_id=account_id)
except Account.DoesNotExist:
    account = None

When raw SQL is unavoidable, use cursor.execute(sql, params) with a params tuple or dict. Never use string formatting to inject values.

# Safe raw query with parameterization
from django.db import connection

def get_accounts_by_country(country):
    with connection.cursor() as cursor:
        cursor.execute(
            "SELECT id, name FROM accounts WHERE country = %s",
            [country]
        )
        return cursor.fetchall()

For extra(), pass select params separately:

# Safe extra with parameters
accounts = Account.objects.extra(
    select={'country_upper': "UPPER(country)"},
    where=["country = %s"],
    params=[country]
)

Avoid raw() with unsanitized input. If you must use raw(), parameterize strictly:

# Safe raw() with parameters
accounts = Account.objects.raw(
    "SELECT * FROM accounts WHERE country = %s AND status = %s",
    [country, status]
)

CockroachDB-Specific Considerations

CockroachDB’s PostgreSQL compatibility means standard Django database backends work, but ensure your database engine is set to django.contrib.gis.db.backends.postgis or a compatible backend if using PostGIS extensions. Use CockroachDB connection parameters in settings as you would for PostgreSQL:

# settings.py example for CockroachDB
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'mydb',
        'USER': 'myuser',
        'PASSWORD': 'mypassword',
        'HOST': 'cockroachdb-host.mycompany.com',
        'PORT': '26257',
        'OPTIONS': {
            'sslmode': 'require',
        },
    }
}

Validate and sanitize inputs before they reach the ORM. Use Django forms and model field validation to constrain data types and lengths. For dynamic column or table names (which cannot be parameterized), use an allowlist approach:

# Safe dynamic column name with allowlist
def dynamic_sort(ordering):
    allowed = {'created_at', 'name', 'email'}
    if ordering not in allowed:
        raise ValueError('Invalid ordering field')
    return Account.objects.order_by(ordering)

Finally, test your queries against CockroachDB to confirm the generated SQL is as expected. Use Django’s connection.queries in development (with DEBUG=True) to review that parameters are being passed separately and not interpolated into the SQL string.

Related CWEs: inputValidation

CWE IDNameSeverity
CWE-20Improper Input Validation HIGH
CWE-22Path Traversal HIGH
CWE-74Injection CRITICAL
CWE-77Command Injection CRITICAL
CWE-78OS Command Injection CRITICAL
CWE-79Cross-site Scripting (XSS) HIGH
CWE-89SQL Injection CRITICAL
CWE-90LDAP Injection HIGH
CWE-91XML Injection HIGH
CWE-94Code Injection CRITICAL

Frequently Asked Questions

Can CockroachDB's native escaping features replace parameterized queries in Django?
No. CockroachDB does not perform client-side escaping that would safely neutralize injection payloads when values are embedded into the SQL string. Always use parameterized queries via Django’s ORM or cursor.execute with params.
Is using ORM in Django completely safe against SQL injection with CockroachDB?
Yes, when you use the ORM correctly (filter, exclude, get) it generates parameterized SQL. Risk arises only when you bypass the ORM with string-concatenated raw SQL or unsafe extra/raw calls.