it-swarm.com.de

Wie können vorbereitete Anweisungen vor SQL-Injection-Angriffen schützen?

Wie helfen uns preparierte Anweisungen uns, SQL Injection Angriffe zu verhindern?

Wikipedia sagt:

Vorbereitete Anweisungen sind gegen SQL-Injection widerstandsfähig, da Parameterwerte, die später mit einem anderen .__ übertragen werden. Protokoll muss nicht korrekt escapiert werden. Wenn die ursprüngliche Aussage Die Vorlage wird nicht von externen Eingaben abgeleitet, die SQL-Injection kann nicht auftreten.

Ich kann den Grund nicht sehr gut sehen. Was wäre eine einfache Erklärung in leichtem Englisch und einigen Beispielen?

132
Aan

Die Idee ist sehr einfach - die Abfrage und die Daten werden an den Datenbankserver separat gesendet.
Das ist alles.

Die Wurzel des SQL-Injection-Problems ist Mischen des Codes und der Daten. 

Tatsächlich ist unsere SQL-Abfrage ein legitimes Programm. Und wir erstellen ein solches Programm dynamisch, indem wir Daten hinzufügen. Daher können diese Daten den -Programmcode stören und sogar ändern, wie jedes SQL-Injektionsbeispiel zeigt (alle Beispiele in PHP/Mysql):

$expected_data = 1;
$query = "SELECT * FROM users where id=$expected_data";

wird eine regelmäßige Abfrage erzeugen 

SELECT * FROM users where id=1

während dieser Code

$spoiled_data = "1; DROP TABLE users;"
$query        = "SELECT * FROM users where id=$spoiled_data";

wird eine schädliche Sequenz erzeugen

SELECT * FROM users where id=1; DROP TABLE users;

Es funktioniert, weil wir die Daten direkt in den Programmhauptteil einfügen und Teil des Programms werden. Daher können die Daten das Programm ändern, und abhängig von den übergebenen Daten wird entweder eine reguläre Ausgabe oder eine Tabelle users gelöscht.

Während bei vorbereiteten Anweisungen ändern wir unser Programm nicht, bleibt es jedoch intakt.
Das ist der Punkt. 

Wir senden zuerst ein -Programm an den Server

$db->prepare("SELECT * FROM users where id=?");

wobei die Daten durch einige variable ersetzt werden, die als Parameter oder Platzhalter bezeichnet werden.

Beachten Sie, dass genau dieselbe Abfrage an den Server gesendet wird, ohne dass sich darin Daten befinden! Und dann senden wir die Daten mit der Anfrage second, im Wesentlichen getrennt von der Abfrage selbst:

$db->execute($data);

so kann es unser Programm nicht verändern und Schaden anrichten.
Ganz einfach - nicht wahr? 

Es ist jedoch zu beachten, dass nicht jedes Mal Sie einen Platzhalter verwenden, wird als vorbereitete Anweisung verarbeitet.

Ein Platzhalter ist eine allgemeine Idee, um die tatsächlichen Daten durch eine Variable für die zukünftige Verarbeitung zu ersetzen (siehe beispielsweise printf()), während eine vorbereitete Anweisung nur eine Teilmenge davon ist. 

Es gibt Fälle (insbesondere PDO in PHP kann dies), wenn eine vorbereitete Anweisung emuliert werden kann und eine Abfrage tatsächlich zusammen mit Daten zusammengestellt und in einer Anforderung an den Server gesendet wird. Es ist jedoch wichtig zu verstehen, dass dieser Ansatz gleichermaßen sicher ist, da jedes Datenbit entsprechend seinem Typ ordnungsgemäß formatiert ist und daher nichts Falsches passieren könnte.

Das einzige, was ich hinzufügen muss, ist in jedem Handbuch immer weggelassen:

Vorbereitete Anweisungen können nur data schützen, aber kann das Programm selbst nicht verteidigen.
Wenn wir also zum Beispiel einen dynamischen Bezeichner hinzufügen müssen - ein Feldname, zum Beispiel, können uns vorbereitete Anweisungen nicht helfen. Ich habe die Angelegenheit kürzlich erklärt , also werde ich mich nicht wiederholen. 

245

Hier ist SQL zum Einrichten eines Beispiels:

CREATE TABLE employee(name varchar, paymentType varchar, amount bigint);

INSERT INTO employee VALUES('Aaron', 'salary', 100);
INSERT INTO employee VALUES('Aaron', 'bonus', 50);
INSERT INTO employee VALUES('Bob', 'salary', 50);
INSERT INTO employee VALUES('Bob', 'bonus', 0);

Die Inject-Klasse ist anfällig für SQL-Injection. Die Abfrage wird zusammen mit der Benutzereingabe dynamisch eingefügt. Ziel der Abfrage war es, Informationen zu Bob anzuzeigen. Entweder Gehalt oder Bonus, basierend auf Benutzereingaben. Der böswillige Benutzer manipuliert jedoch die Eingabe und beschädigt die Abfrage, indem er das Äquivalent eines 'oder wahr' der where-Klausel anfügt, sodass alles zurückgegeben wird, einschließlich der Informationen über Aaron, die verborgen sein sollten.

import Java.sql.*;

public class Inject {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=user&password=pwd";
        Connection conn = DriverManager.getConnection(url);

        Statement stmt = conn.createStatement();
        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='" + args[0] + "'";
        System.out.println(sql);
        ResultSet rs = stmt.executeQuery(sql);

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

Der erste Fall ist normal, der zweite Fall ist die bösartige Injektion:

c:\temp>Java Inject salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary'
salary 50

c:\temp>Java Inject "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType='salary' OR 'a'!='b'
salary 100
bonus 50
salary 50
bonus 0

Sie sollten Ihre SQL-Anweisungen nicht mit Zeichenfolgenverkettung von Benutzereingaben erstellen. Es ist nicht nur anfällig für die Injektion, sondern hat auch Auswirkungen auf das Caching auf dem Server (die Anweisung ändert sich, daher ist es weniger wahrscheinlich, dass ein SQL-Anweisungscachetreffer auftritt, während das Bindungsbeispiel immer dieselbe Anweisung ausführt).

Hier ist ein Beispiel für die Bindung, um diese Art von Injektion zu vermeiden:

import Java.sql.*;

public class Bind {

    public static void main(String[] args) throws SQLException {

        String url = "jdbc:postgresql://localhost/postgres?user=postgres&password=postgres";
        Connection conn = DriverManager.getConnection(url);

        String sql = "SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?";
        System.out.println(sql);

        PreparedStatement stmt = conn.prepareStatement(sql);
        stmt.setString(1, args[0]);

        ResultSet rs = stmt.executeQuery();

        while (rs.next()) {
            System.out.println(rs.getString("paymentType") + " " + rs.getLong("amount"));
        }
    }
}

Die Ausführung mit derselben Eingabe wie im vorherigen Beispiel zeigt, dass der bösartige Code nicht funktioniert, da es keinen Zahlungstyp gibt, der mit dieser Zeichenfolge übereinstimmt:

c:\temp>Java Bind salary
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
salary 50

c:\temp>Java Bind "salary' OR 'a'!='b"
SELECT paymentType, amount FROM employee WHERE name = 'bob' AND paymentType=?
20
Glenn

Bei vorbereiteten Anweisungen werden die Daten, die von einem potenziellen Hacker eingehen, grundsätzlich als Daten behandelt - und es ist unmöglich, sie mit Ihrer Anwendungs-SQL zu mischen und/oder als SQL zu interpretieren (was passieren kann, wenn die übergebenen Daten direkt in Ihre Datenbank eingefügt werden Anwendung SQL).

Dies liegt daran, dass vorbereitete Anweisungen zuerst die SQL-Abfrage "vorbereiten", um einen effizienten Abfrageplan zu finden, und die tatsächlichen Werte senden, die vermutlich aus einem Formular stammen - zu diesem Zeitpunkt wird die Abfrage tatsächlich ausgeführt. 

Weitere tolle Infos hier:

Vorbereitete Anweisungen und SQL Injection

13
Jose

Wenn Sie eine vorbereitete Anweisung erstellen und an das DBMS senden, wird diese zur Ausführung als SQL-Abfrage gespeichert.

Sie binden Ihre Daten später an die Abfrage, sodass das DBMS diese Daten als Abfrageparameter für die Ausführung (Parametrisierung) verwendet. Das DBMS verwendet die von Ihnen gebundenen Daten nicht als Ergänzung zu der bereits kompilierten SQL-Abfrage. Es sind einfach die Daten.

Das bedeutet, dass es grundsätzlich unmöglich ist, SQL-Injection mit vorbereiteten Anweisungen auszuführen. Die Natur der vorbereiteten Anweisungen und ihre Beziehung zum DBMS verhindern dies.

5
wulfgarpro

In SQL Server ist die Verwendung einer vorbereiteten Anweisung definitiv injektionssicher, da die Eingabeparameter die Abfrage nicht bilden. Dies bedeutet, dass es sich bei der ausgeführten Abfrage nicht um eine dynamische Abfrage handelt. Beispiel einer anfälligen SQL-Injection-Anweisung.

string sqlquery = "select * from table where username='" + inputusername +"' and password='" + pass + "'";

Wenn der Wert in der Variablen inoutusername nun etwa 'oder 1 = 1 ist -, wird diese Abfrage jetzt zu:

select * from table where username='a' or 1=1 -- and password=asda

Der Rest wird nach -- kommentiert, sodass er niemals ausgeführt und umgangen wird, wie im unten dargestellten vorbereiteten Anweisungsbeispiel.

Sqlcommand command = new sqlcommand("select * from table where username = @userinput and [email protected]");
command.Parameters.Add(new SqlParameter("@userinput", 100));
command.Parameters.Add(new SqlParameter("@pass", 100));
command.prepare();

In der Tat können Sie also keinen anderen Parameter einschicken, um die SQL-Injection zu vermeiden ...

4
lloydom

Die Schlüsselphrase lautet need not be correctly escaped. Das bedeutet, dass Sie sich keine Sorgen machen müssen, dass Leute versuchen, Bindestriche, Apostrophe, Zitate usw. einzuwerfen.

Es wird alles für Sie erledigt.

3
Feisty Mango
ResultSet rs = statement.executeQuery("select * from foo where value = " + httpRequest.getParameter("filter");

Nehmen wir an, Sie haben das in einem Servlet. Wenn eine böswillige Person einen falschen Wert für "Filter" übergeben hat, hacken Sie möglicherweise Ihre Datenbank.

2
MeBigFatGuy

Ich las die Antworten durch und fühlte immer noch das Bedürfnis, den Schlüsselpunkt hervorzuheben, der die Essenz von Prepared Statements beleuchtet. Überlegen Sie sich zwei Möglichkeiten, die eigene Datenbank in Bezug auf Benutzereingaben abzufragen:

Naiver Ansatz

Man verkettet Benutzereingaben mit einer partiellen SQL-Zeichenfolge, um eine SQL-Anweisung zu generieren. In diesem Fall kann der Benutzer schädliche SQL-Befehle einbetten, die zur Ausführung an die Datenbank gesendet werden.

String SQLString = "SELECT * FROM CUSTOMERS WHERE NAME='"+userInput+"'"

Beispielsweise kann eine böswillige Benutzereingabe dazu führen, dass SQLString gleich "SELECT * FROM CUSTOMERS WHERE NAME='James';DROP TABLE CUSTOMERS;' ist.

Aufgrund des böswilligen Benutzers enthält SQLString zwei Anweisungen, bei denen die zweite ("DROP TABLE CUSTOMERS") Schaden anrichtet.

Vorbereitete Anweisungen

In diesem Fall wird die Benutzereingabe aufgrund der Trennung von Abfrage und Daten niemals als SQL-Anweisung behandelt und daher niemals ausgeführt. Aus diesem Grund würde jeder eingefügte schädliche SQL-Code keinen Schaden verursachen. Der "DROP TABLE CUSTOMERS" würde also im obigen Fall niemals ausgeführt.

Kurz gesagt: Mit vorbereiteten Anweisungen wird über Benutzereingaben eingeführter bösartiger Code nicht ausgeführt!

1
N.Vegeta

Grundursache # 1 - Das Trennzeichenproblem

SQL-Injektion ist möglich, weil wir Anführungszeichen verwenden, um Strings zu begrenzen und auch Teile von Strings zu sein, sodass sie manchmal nicht interpretiert werden können. Wenn wir Trennzeichen hätten, die in String-Daten nicht verwendet werden könnten, wäre die SQL-Injektion niemals vorgekommen. Durch Lösen des Begrenzerproblems wird das Problem der SQL-Injektion behoben. Strukturabfragen machen das.

Root Cause # 2 - Die menschliche Natur, die Leute sind schlau und Einige schlauen Leute sind bösartigUnd alle Leute machen Fehler

Die andere Hauptursache der SQL-Injektion ist die menschliche Natur. Menschen, einschließlich Programmierer, machen Fehler. Wenn Sie bei einer strukturierten Abfrage einen Fehler machen, wird Ihr System nicht anfällig für die SQL-Injektion. Wenn Sie keine strukturierten Abfragen verwenden, können Fehler zu einer SQL-Injektionsanfälligkeit führen.

Wie strukturierte Abfragen die Hauptursachen der SQL-Injektion beheben

Strukturierte Abfragen Lösen Sie das Begrenzerproblem, indem Sie SQL-Befehle in eine Anweisung und die Daten in eine separate Programmieranweisung einfügen. Programmieranweisungen schaffen die erforderliche Trennung.

Strukturierte Abfragen verhindern, dass durch menschliche Fehler kritische Sicherheitslücken entstehen. In Bezug auf Fehler, die der Mensch macht, kann keine SQL-Injektion erfolgen, wenn Strukturabfragen verwendet werden. Es gibt Möglichkeiten, die SQL-Injektion zu verhindern, die keine strukturierten Abfragen beinhalten, aber normale menschliche Fehler führen bei diesen Ansätzen normalerweise zu einer gewissen Exposition gegenüber der SQL-Injektion. Strukturierte Abfragen sind aus der SQL-Injektion nicht sicher. Sie können alle Fehler in der Welt beinahe mit strukturierten Abfragen wie bei jeder anderen Programmierung machen, aber jeder, den Sie machen können, kann in ein durch SQL-Injektion übernommenes System umgewandelt werden. Deshalb sagen die Leute gerne, dass dies der richtige Weg ist, um die Injektion von SQL zu verhindern.

Also haben Sie es, die Ursachen der SQL-Injektion und die strukturierten Abfragen, die sie bei ihrer Verwendung unmöglich machen.

0
DanAllen