Bei der Suche nach einer Lösung für Django ORM Bestellung von genau , ich habe eine benutzerdefinierte Django Func:
%Vor%was wie folgt funktioniert:
%Vor%Aber wie @hynekcer kommentierte:
"Es stürzt leicht ab von
') in '') from myapp_suburb; drop ...
erwartet, dass der Name der App "myapp and autocommit ist aktiviert."
Das Hauptproblem besteht darin, dass zusätzliche Daten ( substring
) ohne sqlescape in die Vorlage gelangen, was die Anwendung anfällig für SQL-Injection-Angriffe macht.
Ich kann nicht finden, welches der Django-Weg davor ist.
Ich habe ein Repo (djposfunc) erstellt, in dem Sie jede Lösung testen können.
TL; DR:
Alle Beispiele mit Func()
in Django Dokumente können problemlos verwendet werden, um andere ähnliche SQL-Funktionen mit einem Argument sicher zu implementieren.
Alle eingebauten Django Datenbankfunktionen und conditional functions , die Nachkommen von Func()
sind, sind ebenfalls sicher. Eine Anwendung über dieses Limit hinaus erfordert einen Kommentar.
Die Klasse Func () ist der allgemeinste Teil von Django Query-Ausdrücken. Es erlaubt, fast jede Funktion oder jeden Operator in Django ORM zu implementieren. Es ist wie ein Schweizer Taschenmesser , sehr universell, aber man muss etwas aufmerksamer sein, um sich nicht zu schneiden, als mit einem spezialisierten Werkzeug (wie einem elektrischen Messer mit optischer Barriere). Noch sicherer ist es dann, mit einem Hammer aus einem Stück Eisen ein eigenes Werkzeug zu schmieden, wenn einmal ein "verbessertes" "sicheres" Taschenmesser nicht in die Tasche passt.
Sicherheitshinweis
Die kurze Dokumentation für Func(*expressions, **extra)
mit Beispielen sollte zuerst gelesen werden. (Ich empfehle hier die Entwicklungsdokumente für Django 2.0, wo kürzlich weitere Sicherheitsinformationen hinzugefügt wurden, einschließlich Vermeidung von SQL-Injection , genau bezogen auf Ihr Beispiel.)
Alle positionalen Argumente in *expressions
werden von Django kompiliert , dh Value(string)
werden in Parameter verschoben, wo sie vom Datenbanktreiber korrekt gemerkt werden .
F(name)
interpretiert, dann wird rechts mit table_name.
alias dot vorangestellt, eventuell wird ein Join zu dieser Tabelle hinzugefügt und die Namen werden von quote_name()
function. **extra
und **extra_context
sind vage dokumentiert. Sie können nur für einfache Parameter verwendet werden, die niemals "kompiliert" werden und niemals SQL params
durchlaufen. Zahlen oder einfache Strings mit sicheren Zeichen ohne Apostroph, Backslash oder Prozent sind gut. Es kann kein Feldname sein, weil es nicht eindeutig ist, weder verbunden noch. Es ist sicher für zuvor überprüfte Nummern und feste Zeichenfolgen wie 'ASC' / 'DESC', Zeitzonennamen und andere Werte wie aus einer Dropdown-Liste. Es gibt immer noch einen Schwachpunkt. Dropdown-Listenwerte müssen auf der Serverseite überprüft werden. Außerdem müssen Zahlen verifiziert werden, dass es sich um Zahlen handelt, nicht um eine numerische Zeichenfolge wie '2'
, da alle Datenbankfunktionen stillschweigend eine ausgelassene numerische Zeichenfolge anstelle der Zahl akzeptieren. Wenn eine falsche "Zahl" übergeben wird '0) from my_app.my_table; rogue_sql; --'
, dann ist die Injektion beendet. Beachten Sie, dass die Rogue-Zeichenfolge in diesem Fall kein sehr einschränkendes Zeichen enthält. Vom Benutzer bereitgestellte Nummern müssen speziell überprüft werden, oder der Wert muss durch die Position expressions
. function
name und arg_joiner
string Attribute der Func Klasse oder die gleichen function
und arg_joiner
Parameter des Func () Aufrufs anzugeben. Der Parameter template
darf keine Apostrophe um ersetzte Parameterausdrücke in Klammern enthalten: (
%(expressions)s
)
, da Apostrophen bei Bedarf vom Datenbanktreiber hinzugefügt werden, aber zusätzliche Apostrophe können dazu führen, dass er normalerweise nicht korrekt funktioniert , aber manchmal könnte es übersehen werden, und das würde zu einem weiteren Sicherheitsproblem führen . Hinweise, die nicht mit der Sicherheit zusammenhängen
Viele einfache eingebaute Funktionen mit einem Argument sehen nicht so einfach wie möglich aus, da sie von Mehrzweck-Nachkommen von Func abgeleitet sind. Zum Beispiel Length
ist eine Funktion, die auch als Nachschlag verwendet werden kann Transform
.
Lookup-Transformation wendet die gleiche Funktion auf die linke und rechte Seite der Suche an.
%Vor% Die gleichen Schlüsselwortargumente, die in Func.as_sql(..., function=..., template=..., arg_joiner=...)
angegeben werden können, können bereits in Func.__init__()
angegeben werden, wenn sie nicht im benutzerdefinierten as_sql () überschrieben werden. Sie können auch als Attribute einer benutzerdefinierten untergeordneten Klasse von Func
festgelegt werden. .
Viele SQL-Datenbankfunktionen haben eine ausführliche Syntax wie POSITION(substring IN string)
, weil es die Lesbarkeit vereinfacht, wenn benannte Parameter nicht wie POSITION( IN )
und eine kurze Variante STRPOS(string, substring)
(por postgres) oder INSTR(string, substring)
(für andere) unterstützt werden Datenbanken), die einfacher von Func()
implementiert werden können und die Lesbarkeit durch den Python-Wrapper mit __init__(expression, substring)
.
Auch sehr komplizierte Funktionen können durch eine Kombination von verschachtelten Funktionen mit einfachen Argumenten sicher implementiert werden: Case(When(field_name=lookup_value, then=Value(value)), When(...),... default=Value(value))
.
basierend auf den Ideen von John Moutafis, ist die letzte Funktion (innerhalb der Methode __init__
verwenden wir Values
für das Sicherheitsergebnis.)
Normalerweise sind Sie anfällig für einen SQL-Injection-Angriff. die" stray "einfachen Anführungszeichen '
.
Alles, was zwischen einem einzelnen Quote-Paar enthalten ist, wird so verarbeitet, wie es sollte, aber ein ungepaartes einzelnes Anführungszeichen kann die Zeichenfolge beenden und dem Rest des Eintrags erlauben, als ausführbares Code-Stück zu fungieren.
Das ist bei @ hynekcers Beispiel genau der Fall.
Django stellt die Methode Value
zur Verfügung das oben genannte:
Der Wert wird zur SQL-Parameterliste hinzugefügt und richtig zitiert .
Wenn Sie also sicherstellen, dass alle Benutzereingaben über die Value
-Methode übergeben werden, sind Sie in Ordnung:
BEARBEITEN:
Wie in den Kommentaren erwähnt, scheint das in der obigen Einstellung nicht wie erwartet zu funktionieren. Aber wenn der Anruf wie folgt ist, funktioniert es:
%Vor%Als Nebenbemerkung: Warum sollten Sie sich zuerst mit den Vorlagen anlegen?
Sie können die Funktion, die Sie verwenden möchten, aus einer beliebigen Datenbanksprache (z. B. SQLite, PostgreSQL, MySQL usw.) finden und explizit verwenden:
%Vor%BEARBEITEN:
Sie können andere Funktionen (wie die Funktion LOWER
) in einem Func
-Aufruf wie folgt verwenden:
Tags und Links python django django-queryset django-annotate