FlawOpen

flawopen.com/SQL Injection em Python/Django ORM

O ORM do Django é seguro contra SQL Injection?

Página de referência
Resposta curta

A API QuerySet do Django separa o SQL dos valores e envia parâmetros ao driver do banco nas consultas comuns, como .filter(), .get() e .exclude(). Isso não protege todas as formas de montar consultas: SQL bruto, expressões personalizadas e estrutura da consulta definida pelo usuário exigem revisão própria.

Uso seguro de valores em QuerySets do Django

INSEGURO — interpolar entrada no SQL bruto
username = request.GET.get("u", "")
User.objects.raw(
    f"SELECT * FROM myapp_user WHERE username = '{username}'"
)
SEGURO — vincular entrada com params de raw()
username = request.GET.get("u", "")
User.objects.raw(
    "SELECT * FROM myapp_user WHERE username = %s",
    [username],
)

Onde a proteção exige atenção

Model.objects.raw()

Manager.raw() executa o SQL fornecido. Passe valores controlados pelo usuário por params e deixe os placeholders sem aspas, por exemplo User.objects.raw("SELECT * FROM users WHERE username = %s", [username]). Não insira valores no SQL com f-strings, formatação % ou concatenação. Parâmetros representam valores, não identificadores SQL, como nomes de tabelas ou colunas.

Acesso direto com connection.cursor()

No acesso direto ao banco, use cursor.execute(sql, params) e passe os valores separadamente conforme os placeholders documentados pelo Django. Não interpole valores em sql. Revise também RawSQL e expressões Func: contexto de template e argumentos nomeados podem ser interpolados no SQL, em vez de vinculados como parâmetros.

Nomes de campos controlados pelo usuário

Um caminho de campo controlado pelo usuário em .filter(**{field: value}) não é automaticamente SQL Injection: o Django interpreta nomes de lookup como estrutura da consulta. Ainda assim, pode expor campos, relações ou lookups que o endpoint não deveria oferecer, além de causar erros. Mapeie opções externas de ordenação e filtro para uma allow-list restrita de campos e lookups permitidos.

Como auditar consultas Django

rg -n '(\.raw\(|RawSQL\(|\.extra\(|connection\.cursor\()' --glob '*.py' .
Pesquise usos de .raw(), RawSQL, .extra() e connection.cursor(). Revise cada ocorrência e rastreie os valores até a consulta; verifique parâmetros vinculados, fragmentos SQL não confiáveis, identificadores e lookups dinâmicos, além de templates customizados Func.

Perguntas frequentes

extra() é seguro?

QuerySet.extra() é uma API legada para adicionar fragmentos SQL. Evite-a em código novo e revise com cuidado os argumentos where e select existentes. Prefira expressões do QuerySet; quando SQL bruto for necessário, vincule valores pelo argumento documentado params.

Isso também se aplica ao Django REST Framework?

O Django REST Framework não altera a parametrização SQL do ORM. As mesmas regras se aplicam a consultas de views, serializers e viewsets; também use allow-lists para campos de ordenação ou filtro controlados pelo cliente.

Referências

Veja também SQL Injection (CWE-89): como funciona e como evitar em todas as linguagens — flawopen.com Injeção SQL no Doctrine ORM: consultas seguras EF Core FromSqlRaw: SQL parametrizado seguro FastAPI e SQL Injection: o que o Pydantic protege GORM Raw SQL: evite injeção SQL em Go É suficiente escapar aspas contra SQL Injection? Usar um ORM basta para prevenir SQL Injection?