FlawOpen

flawopen.com/SQL Injection in Python/Django ORM

Schützt das Django-ORM standardmäßig vor SQL Injection?

Referenzseite
Kurzantwort

Die QuerySet-API von Django trennt bei üblichen Abfragen wie .filter(), .get() und .exclude() den SQL-Code von den Werten und übergibt Werte als Parameter an den Datenbanktreiber. Dieser Schutz deckt nicht jede Art des Abfrageaufbaus ab: Raw SQL, benutzerdefinierte Ausdrücke und vom Benutzer bestimmte Query-Strukturen brauchen eine eigene Prüfung.

Werte sicher in Django-QuerySets verwenden

UNSICHER — Eingabe in Raw-SQL interpolieren
username = request.GET.get("u", "")
User.objects.raw(
    f"SELECT * FROM myapp_user WHERE username = '{username}'"
)
SICHER — Eingabe mit raw()-params binden
username = request.GET.get("u", "")
User.objects.raw(
    "SELECT * FROM myapp_user WHERE username = %s",
    [username],
)

Wo der Schutz besondere Sorgfalt erfordert

Model.objects.raw()

Manager.raw() führt das übergebene SQL aus. Übergib benutzergesteuerte Werte über params und setze Platzhalter nicht in Anführungszeichen, etwa User.objects.raw("SELECT * FROM users WHERE username = %s", [username]). Füge Werte nicht mit f-Strings, %-Formatierung oder Verkettung in SQL ein. Parameter stehen für Werte, nicht für SQL-Bezeichner wie Tabellen- oder Spaltennamen.

Direkter Zugriff mit connection.cursor()

Verwende beim direkten Datenbankzugriff cursor.execute(sql, params) und übergib Werte separat mit den dokumentierten Django-Platzhaltern. Interpoliere keine Werte in sql. Prüfe auch RawSQL und Func-Ausdrücke: Template-Kontext und benannte Argumente können in SQL interpoliert statt als Parameter gebunden werden.

Benutzergesteuerte Feldpfade

Ein benutzergesteuerter Feldpfad in .filter(**{field: value}) ist nicht automatisch SQL Injection: Django löst Lookup-Namen als Query-Struktur auf. Trotzdem kann dies Felder, Beziehungen oder Lookups offenlegen, die der Endpunkt nicht anbieten sollte, und Fehler auslösen. Ordne externe Sortier- und Filteroptionen einer engen Allowlist erlaubter Modellfelder und Lookups zu.

Django-Abfragen prüfen

rg -n '(\.raw\(|RawSQL\(|\.extra\(|connection\.cursor\()' --glob '*.py' .
Suche nach .raw(), RawSQL, .extra() und connection.cursor(). Prüfe jeden Treffer und verfolge Werte bis zur Abfrage: gebundene Parameter, nicht vertrauenswürdige SQL-Fragmente, dynamische Bezeichner oder Lookups sowie benutzerdefinierte Func-Templates.

Häufige Fragen

Ist extra() sicher?

QuerySet.extra() ist eine ältere API zum Ergänzen roher SQL-Fragmente. Verwende sie nicht in neuem Code und prüfe bestehende Argumente für where und select sorgfältig. Bevorzuge QuerySet-Ausdrücke; wenn Raw SQL nötig ist, binde Werte über das dokumentierte Argument params.

Gilt das auch für Django REST Framework?

Django REST Framework ändert die SQL-Parametrisierung des ORM nicht. Dieselben Regeln gelten für Abfragen aus Views, Serializers und Viewsets. Begrenze außerdem clientgesteuerte Sortier- und Filterfelder mit einer Allowlist.

Quellen

Siehe auch SQL-Injection (CWE-89): Funktionsweise und Schutz in jeder Sprache — flawopen.com Doctrine ORM: SQL-Injection vermeiden EF Core FromSqlRaw: Sicheres SQL mit Parametern FastAPI SQL Injection: Was Pydantic verhindert GORM Raw SQL: SQL-Injection in Go verhindern Reicht das Escapen von Anführungszeichen gegen SQL Injection? Reicht ein ORM aus, um SQL-Injection zu verhindern?