
Django SQL-Injection-Schwachstelle
Django ist ein Open-Source-Webanwendungs-Framework, das in Python geschrieben und nach dem MVC-Modell (Model-View-Controller) aufgebaut ist. Ursprünglich wurde es zur Verwaltung von Nachrichten-Webseiten entwickelt, die dem Verlagskonzern Lawrence gehören, als Content-Management-System (CMS).
Django-Versionen 3.1.x bis 3.1.13 und 3.2.x bis 3.2.5 weisen eine SQL-Injection-Sicherheitslücke auf.
Die Ursache dieser Sicherheitslücke ist, dass die Filterfunktion für vom Benutzer kontrollierte Eingabedaten in QuerySet.order_by() nicht ausreicht, um SQL-Injection-Angriffe zu verhindern. Diese Sicherheitslücke kann ausgenutzt werden, um unbefugte Aktionen durchzuführen, die zur Offenlegung sensibler Daten führen.
| CVE - ID | CVE-2021-35042 |
|---|---|
| Severity | 9.8 - KRITISCH |
| CWE - ID | CWE-89: Unzureichende Neutralisierung von speziellen Elementen in einem SQL-Befehl ('SQL-Injection') |
| Vulnerability Publication Date | 1.7.2021 |
| Affected Software | 3.1.x < 3.1.13, 3.2.x < 3.2.5 |
| Require Authentication | Nicht erforderlich |
In Django werden das Erstellen von Tabellen und das Definieren von Feldern in der Datenbank durch Deklarieren einer Modellklasse in der Datei models.py durchgeführt. In diesem Beispiel deklarieren wir eine Tabelle mit dem Namen Wolf und ein Feld mit dem Namen name.


Das in Django integrierte ORM-Framework wird für Datenbankoperationen verwendet, und das Ergebnis einer Abfrage ist eine Sammlung, die als QuerySet bezeichnet wird.
order_by(fields)
Standardmäßig gibt order_by() ein QuerySet zurück, das nach der im ordering-Attribut des Meta-Modells festgelegten Reihenfolge sortiert ist. Wir können die order_by-Bedingung in jeder Abfrage überschreiben, indem wir die Methode order_by() verwenden.
Beispiel
wolves = Wolf.objects.order_by('-name', 'id')
Das Ergebnis der obigen Abfrage wird absteigend nach dem Feld name und dann aufsteigend nach id sortiert. Das Minuszeichen vor dem Feldnamen name zeigt an, dass das Ergebnis absteigend sortiert wird.
Das folgende Beispiel sortiert das zurückgegebene Ergebnis nach dem vom Benutzer empfangenen Feld; wenn kein Wert übergeben wird, wird nach dem Feld
idsortiert.
Ergebnis

In den Versionen 3.1 und 3.2 erlaubt Django das Kombinieren von Abfragemethoden mit Tabellennamen in der order_by-Abfrage. Dies ist auch die Hauptursache für diese Sicherheitslücke.
Die Übergabe eines Tabellennamens liefert das gleiche Ergebnis wie die Übergabe eines Feldnamens.
cve202135042_wolf ist der Tabellenname
Zuerst ruft die Anwendung direkt die Funktion order_by() auf; der Code zur Verarbeitung der Funktion order_by() ist definiert unter:
django/db/models/query.py

Die Funktion
order_by()führt zwei Aufgaben aus:
- Löscht alle aktuellen Methoden, die durch order_by() aufgerufen werden, und entfernt den übergebenen Standardparameter, wenn order_by einen anderen Wert erhält.
- Übergibt den Parameter an order_by. Die Funktion
add_ordering()führt dies aus.
def add_ordering(self, *ordering):
"""
Add items from the 'ordering' sequence to the query's "order by"
clause. These items are either field names (not column names) --
possibly with a direction prefix ('-' or '?') -- or OrderBy
expressions.
If 'ordering' is empty, clear all ordering from the query.
"""
errors = []
for item in ordering:
if isinstance(item, str):
if '.' in item:
warnings.warn(
'Passing column raw column aliases to order_by() is '
'deprecated. Wrap %r in a RawSQL expression before '
'passing it to order_by().' % item,
category=RemovedInDjango40Warning,
stacklevel=3,
)
continue
if item == '?':
continue
if item.startswith('-'):
item = item[1:]
if item in self.annotations:
continue
if self.extra and item in self.extra:
continue
# names_to_path() validates the lookup. A descriptive
# FieldError will be raise if it's not.
self.names_to_path(item.split(LOOKUP_SEP), self.model._meta)
elif not hasattr(item, 'resolve_expression'):
errors.append(item)
if getattr(item, 'contains_aggregate', False):
raise FieldError(
'Using an aggregate in order_by() without also including '
'it in annotate() is not allowed: %s' % item
)
if errors:
raise FieldError('Invalid order_by arguments: %s' % errors)
if ordering:
self.order_by += ordering
else:
self.default_ordering = False
Der an add_ordering() übergebene Parameter ist ein Array.
Beispiel, wenn der Parameter wie folgt übergeben wird:
wolves = Wolf.objects.order_by( 'name' , 'id' )Dann wird die Anwendung dies in die folgende Datenbankabfrage umwandeln:
SELECT "cve202135042_wolf"."id", "cve202135042_wolf"."name" FROM "cve202135042_wolf" ORDER BY "cve202135042_wolf"."name" ASC, "cve202135042_wolf"."id" ASC
Wenn die Funktion add_ordering aufgerufen wird, überprüft sie jedes Element im Array; wenn es sich um einen String handelt, wird es auf die folgenden 5 Fälle geprüft:
if '.' in item:Es prüft, ob es sich um eine Abfrage mit einem Spaltennamen handelt, bei der die Spalte einen Tabellennamen in der SQL-Anweisung hat. Wenn ja, wird eine Warnung ausgegeben undcontinueausgeführt.if item == '?':Wenn der Wert des Elements '?' ist, wird das Ergebnis zufällig sortiert,continue.if item.startswith('-'):Wenn das Element mit dem Zeichen '-' beginnt, wird das Abfrageergebnis absteigend (DESC) sortiert.if item in self.annotations:Es prüft, ob es einen Kommentar enthält; wenn ja,continue.if self.extra and item in self.extra:Bestimmt, ob es zusätzliche Erweiterungen gibt, und wenn ja,continue.
Nach diesen fünf Prüfungen wird der Parameter an die Funktion self.names_to_path(item.split(LOOKUP_SEP), self.model._meta) übergeben, um weiter zu prüfen, ob es sich um einen gültigen Spaltennamen handelt. Wenn gültig, werden sie zur self.ordering der Query-Klasse hinzugefügt, um weiterverarbeitet zu werden.