Wie vermeidet oder verhindert ein PreparedStatement die SQL-Injection?

122

Ich weiß, dass PreparedStatements SQL Injection vermeiden / verhindern. Wie macht es das? Wird die endgültige Formularabfrage, die mit PreparedStatements erstellt wird, eine Zeichenfolge oder eine andere sein?

Prabhu R.
quelle
3
Technisch gesehen besteht die JDBC-Spezifikation nicht darauf, dass keine SQL-Injection-Fehler vorliegen. Ich kenne keine betroffenen Laufwerke.
Tom Hawtin - Tackline
3
@ Jayesh Ich schlage vor, Ihre Blog-Inhalte als Antwort hier hinzuzufügen. Die meisten Antworten zeigen nur die Unterschiede zwischen der Erzeugung dynamischer SQL-Abfragen und dem vorbereiteten stmt. Sie befassen sich nicht mit der Frage, warum vorbereitete Aussagen besser funktionieren als Ihr Blog.
Pavan Manjunath
1
Als Antwort hinzugefügt, hoffe ich, dass es hilft.
Jayesh

Antworten:

78

Das Problem bei der SQL-Injection besteht darin, dass eine Benutzereingabe als Teil der SQL-Anweisung verwendet wird. Mithilfe vorbereiteter Anweisungen können Sie erzwingen, dass die Benutzereingaben als Inhalt eines Parameters (und nicht als Teil des SQL-Befehls) behandelt werden.

Wenn Sie jedoch die Benutzereingabe nicht als Parameter für Ihre vorbereitete Anweisung verwenden, sondern stattdessen Ihren SQL-Befehl durch Zusammenfügen von Zeichenfolgen erstellen, sind Sie auch bei Verwendung vorbereiteter Anweisungen für SQL-Injektionen anfällig .

Tangens
quelle
1
Sicher, aber Sie können immer noch einige oder alle Ihrer Parameter fest codieren.
Tangens
16
Beispiel bitte - Wenn Sie jedoch die Benutzereingabe nicht als Parameter für Ihre vorbereitete Anweisung verwenden, sondern stattdessen Ihren SQL-Befehl durch Zusammenfügen von Zeichenfolgen erstellen, sind Sie auch bei Verwendung vorbereiteter Anweisungen anfällig für SQL-Injektionen.
David Blaine
4
FWIW Vorbereitete Anweisungen sind keine JDBC-Sache - sie sind eine SQL-Sache. Sie können vorbereitete Anweisungen in einer SQL-Konsole vorbereiten und ausführen. PreparedStatement unterstützt sie nur innerhalb von JDBC.
Beldaz
198

Betrachten Sie zwei Möglichkeiten, um dasselbe zu tun:

PreparedStatement stmt = conn.createStatement("INSERT INTO students VALUES('" + user + "')");
stmt.execute();

Oder

PreparedStatement stmt = conn.prepareStatement("INSERT INTO student VALUES(?)");
stmt.setString(1, user);
stmt.execute();

Wenn "Benutzer" von der Benutzereingabe kam und die Benutzereingabe war

Robert'); DROP TABLE students; --

Dann würden Sie in erster Linie abgespritzt. Im zweiten Fall wären Sie in Sicherheit und Little Bobby Tables würden für Ihre Schule registriert.

Paul Tomblin
quelle
8
Wenn ich es richtig verstanden hätte, wäre die Abfrage im zweiten Beispiel, die ausgeführt wird, tatsächlich: INSERT IN student VALUES ("Robert '); DROP TABLE-Schüler; -") - oder zumindest so etwas. Ist das wahr?
Max
18
Nein, in der ERSTEN Instanz würden Sie diese Aussage erhalten. Im zweiten Fall würde "Robert '); DROP TABLE-Schüler; -" in die Benutzertabelle eingefügt.
Paul Tomblin
3
Das habe ich im zweiten Beispiel (dem "sicheren"), der Zeichenfolge Robert ') gemeint ; DROP TABLE Studenten; - wird im Feld in der Schülertabelle gespeichert. Habe ich noch etwas geschrieben ;)
Max
7
Entschuldigung, das Verschachteln von Anführungszeichen ist etwas, das ich aufgrund dieser Verwirrung zu vermeiden versuche. Deshalb mag ich PreparedStatements mit Parametern.
Paul Tomblin
59
Kleine Bobby Tische. XD Große Referenz
Amalgovinus
128

Um zu verstehen, wie PreparedStatement SQL Injection verhindert, müssen wir die Phasen der Ausführung von SQL Query verstehen.

1. Kompilierungsphase. 2. Ausführungsphase.

Immer wenn die SQL Server-Engine eine Abfrage empfängt, muss sie die folgenden Phasen durchlaufen:

Ausführungsphasen für Abfragen

  1. Analyse- und Normalisierungsphase: In dieser Phase wird die Abfrage auf Syntax und Semantik überprüft. Es wird geprüft, ob in der Abfrage verwendete Referenztabellen und -spalten vorhanden sind oder nicht. Es hat auch viele andere Aufgaben zu erledigen, aber gehen wir nicht ins Detail.

  2. Kompilierungsphase: In dieser Phase werden in Abfragen verwendete Schlüsselwörter wie select, from, where usw. in ein maschinenverständliches Format konvertiert. Dies ist die Phase, in der die Abfrage interpretiert und die entsprechenden auszuführenden Maßnahmen festgelegt werden. Es hat auch viele andere Aufgaben zu erledigen, aber gehen wir nicht ins Detail.

  3. Abfrageoptimierungsplan: In dieser Phase wird ein Entscheidungsbaum erstellt, um herauszufinden, wie Abfragen ausgeführt werden können. Es werden die Anzahl der Möglichkeiten zur Ausführung der Abfrage und die mit jeder Art der Ausführung der Abfrage verbundenen Kosten ermittelt. Es wird der beste Plan für die Ausführung einer Abfrage ausgewählt.

  4. Cache: Der beste im Abfrageoptimierungsplan ausgewählte Plan wird im Cache gespeichert, sodass er beim nächsten Eingehen derselben Abfrage nicht erneut Phase 1, Phase 2 und Phase 3 durchlaufen muss. Wenn die nächste Abfrage eingeht, wird sie direkt im Cache überprüft und von dort zur Ausführung abgeholt.

  5. Ausführungsphase: In dieser Phase wird die angegebene Abfrage ausgeführt und die Daten werden als ResultSetObjekt an den Benutzer zurückgegeben .

Verhalten der PreparedStatement-API bei den obigen Schritten

  1. PreparedStatements sind keine vollständigen SQL-Abfragen und enthalten Platzhalter, die zur Laufzeit durch tatsächlich vom Benutzer bereitgestellte Daten ersetzt werden.

  2. Immer wenn ein PreparedStatment mit Platzhaltern an die SQL Server-Engine übergeben wird, durchläuft es die folgenden Phasen

    1. Analyse- und Normalisierungsphase
    2. Kompilierungsphase
    3. Abfrageoptimierungsplan
    4. Cache (Kompilierte Abfrage mit Platzhaltern wird im Cache gespeichert.)

UPDATE Benutzer set username =? und Passwort =? WO id =?

  1. Die obige Abfrage wird analysiert, mit Platzhaltern als Sonderbehandlung kompiliert, optimiert und zwischengespeichert. Die Abfrage ist zu diesem Zeitpunkt bereits kompiliert und in ein maschinenverständliches Format konvertiert. Wir können also sagen, dass die im Cache gespeicherte Abfrage vorkompiliert ist und nur Platzhalter durch vom Benutzer bereitgestellte Daten ersetzt werden müssen.

  2. Zur Laufzeit, wenn vom Benutzer bereitgestellte Daten eingehen, wird die vorkompilierte Abfrage aus dem Cache abgerufen und die Platzhalter durch vom Benutzer bereitgestellte Daten ersetzt.

PrepareStatementWorking

(Denken Sie daran, dass nach dem Ersetzen von Platzhaltern durch Benutzerdaten die endgültige Abfrage nicht erneut kompiliert / interpretiert wird und die SQL Server-Engine Benutzerdaten als reine Daten behandelt und nicht als SQL, das erneut analysiert oder kompiliert werden muss. Das ist das Schöne an PreparedStatement. )

Wenn die Abfrage nicht erneut kompiliert werden muss, werden alle auf den Platzhaltern ersetzten Daten als reine Daten behandelt und haben für die SQL Server-Engine keine Bedeutung. Sie führt die Abfrage direkt aus.

Hinweis: Es ist die Kompilierungsphase nach der Analysephase, die die Abfragestruktur versteht / interpretiert und ihr ein aussagekräftiges Verhalten verleiht. Im Fall von PreparedStatement wird die Abfrage nur einmal kompiliert und die zwischengespeicherte kompilierte Abfrage wird ständig erfasst, um Benutzerdaten zu ersetzen und auszuführen.

Aufgrund der einmaligen Kompilierungsfunktion von PreparedStatement ist es frei von SQL Injection-Angriffen.

Eine ausführliche Erklärung mit Beispiel finden Sie hier: https://javabypatel.blogspot.com/2015/09/how-prepared-statement-in-java-prevents-sql-injection.html

Jayesh
quelle
3
nette Erklärung
Dheeraj Joshi
4
Wörtlich die vollständigste Antwort auf das WIE es funktioniert Stück
Jouell
Es war sehr hilfreich. Danke für die ausführliche Erklärung.
Unbekannt
26

Die in einem PreparedStatement verwendete SQL wird auf dem Treiber vorkompiliert. Ab diesem Zeitpunkt werden die Parameter als Literalwerte und nicht als ausführbare Teile von SQL an den Treiber gesendet. Daher kann kein SQL mithilfe eines Parameters injiziert werden. Ein weiterer vorteilhafter Nebeneffekt von PreparedStatements (Vorkompilierung + nur Parameter senden) ist eine verbesserte Leistung, wenn die Anweisung auch mit unterschiedlichen Werten für die Parameter mehrmals ausgeführt wird (vorausgesetzt, der Treiber unterstützt PreparedStatements), da der Treiber nicht jeweils SQL-Analyse und -Kompilierung durchführen muss Mal ändern sich die Parameter.

Travis Heseman
quelle
Es muss nicht so implementiert werden, und ich glaube, dass es oft nicht so ist.
Tom Hawtin - Tackline
4
Tatsächlich wird SQL normalerweise in der Datenbank vorkompiliert. Das heißt, in der Datenbank wird ein Ausführungsplan erstellt. Wenn Sie die Abfrage ausführen, wird der Plan mit diesen Parametern ausgeführt. Der zusätzliche Vorteil besteht darin, dass dieselbe Anweisung mit unterschiedlichen Parametern ausgeführt werden kann, ohne dass der Abfrageprozessor jedes Mal einen neuen Plan erstellen muss.
Beldaz
3

Ich denke, es wird eine Saite sein. Die Eingabeparameter werden jedoch an die Datenbank gesendet und die entsprechenden Umwandlungen / Konvertierungen werden angewendet, bevor eine tatsächliche SQL-Anweisung erstellt wird.

Um Ihnen ein Beispiel zu geben, wird möglicherweise versucht, festzustellen, ob CAST / Conversion funktioniert.
Wenn es funktioniert, könnte es eine endgültige Aussage daraus erstellen.

   SELECT * From MyTable WHERE param = CAST('10; DROP TABLE Other' AS varchar(30))

Versuchen Sie ein Beispiel mit einer SQL-Anweisung, die einen numerischen Parameter akzeptiert.
Versuchen Sie nun, eine Zeichenfolgenvariable zu übergeben (mit einem numerischen Inhalt, der als numerischer Parameter akzeptabel ist). Löst es einen Fehler aus?

Versuchen Sie nun, eine Zeichenfolgenvariable zu übergeben (deren Inhalt als numerischer Parameter nicht akzeptabel ist). Schau was passiert?

shahkalpesh
quelle
3

Die vorbereitete Anweisung ist sicherer. Es wird ein Parameter in den angegebenen Typ konvertiert.

Beispielsweise stmt.setString(1, user);wird der userParameter in einen String konvertiert .

Angenommen, der Parameter enthält eine SQL-Zeichenfolge, die einen ausführbaren Befehl enthält : Die Verwendung einer vorbereiteten Anweisung lässt dies nicht zu.

Es fügt Metazeichen (auch bekannt als automatische Konvertierung) hinzu.

Dies macht es sicherer.

Guru R Handa
quelle
2

SQL-Injection: Wenn der Benutzer die Möglichkeit hat, etwas einzugeben, das Teil der SQL-Anweisung sein könnte

Beispielsweise:

String query = "INSERT INTO student VALUES ('" + user + "')"

bei Benutzereingabe „Robert“); DROP TABLE Studenten; - ”als Eingabe verursacht es eine SQL-Injection

Wie eine vorbereitete Aussage verhindert dies?

String query = "INSERT INTO student VALUES ('" + ": name" + "')"

parameters.addValue ("Name", Benutzer);

=> wenn der Benutzer erneut "Robert" eingibt); DROP TABLE Studenten; - “, die Eingabezeichenfolge wird auf dem Treiber als Literalwerte vorkompiliert, und ich denke, sie kann wie folgt umgewandelt werden:

CAST ('Robert'); DROP TABLE Studenten; - 'AS varchar (30))

Am Ende wird die Zeichenfolge buchstäblich als Name in die Tabelle eingefügt.

http://blog.linguiming.com/index.php/2018/01/10/why-prepared-statement-avoids-sql-injection/

Jack
quelle
1
Wenn ich mich nicht irre, würde der Teil CAST(‘Robert’);von CAST(‘Robert’); DROP TABLE students; –‘ AS varchar(30))brechen, dann würde er die Tabelle fallen lassen, wenn dies der Fall wäre. Es stoppt die Injektion, daher glaube ich, dass das Beispiel einfach nicht vollständig genug ist, um das Szenario zu erklären.
Héctor Álvarez
1

PreparedStatement:

1) Die Vorkompilierung und das DB-seitige Caching der SQL-Anweisung führt zu einer insgesamt schnelleren Ausführung und der Möglichkeit, dieselbe SQL-Anweisung in Stapeln wiederzuverwenden.

2) Automatische Verhinderung von SQL-Injection-Angriffen durch integriertes Entkommen von Anführungszeichen und anderen Sonderzeichen. Beachten Sie, dass Sie hierfür eine der PreparedStatement setXxx () -Methoden verwenden müssen, um den Wert festzulegen.

Mukesh Kumar
quelle
1

Wie in diesem Beitrag erläutert , PreparedStatementhilft Ihnen das allein nicht, wenn Sie noch Strings verketten.

Zum Beispiel kann ein betrügerischer Angreifer immer noch Folgendes tun:

  • Rufen Sie eine Sleep-Funktion auf, damit alle Ihre Datenbankverbindungen belegt sind und Ihre Anwendung daher nicht verfügbar ist
  • Extrahieren vertraulicher Daten aus der Datenbank
  • Umgehen der Benutzerauthentifizierung

Nicht nur SQL, sondern auch JPQL oder HQL können kompromittiert werden, wenn Sie keine Bindungsparameter verwenden.

Unterm Strich sollten Sie beim Erstellen von SQL-Anweisungen niemals die Verkettung von Zeichenfolgen verwenden. Verwenden Sie zu diesem Zweck eine dedizierte API:

Vlad Mihalcea
quelle
1
Vielen Dank, dass Sie darauf hingewiesen haben, wie wichtig es ist, die Parameterbindung anstelle von PreparedStatement allein zu verwenden. Ihre Antwort scheint jedoch zu implizieren, dass die Verwendung einer dedizierten API zum Schutz vor SQL-Injection erforderlich ist. Da dies nicht der Fall ist und die Verwendung von PreparedStatement mit Parameterbindung auch funktioniert, möchten Sie eine Neuformulierung vornehmen?
Wild Pottok
-3

In Vorbereitete Anweisungen muss der Benutzer Daten als Parameter eingeben. Wenn der Benutzer einige anfällige Anweisungen wie DROP TABLE oder SELECT * FROM USERS eingibt, sind die Daten nicht betroffen, da diese als Parameter der SQL-Anweisung betrachtet werden

Shreyas K.
quelle
Gleiche Antwort wie die ausgewählte Antwort mit weniger Präzision.
Julien Maret