2769 Stimmen

Wie kann ich SQL-Injection in PHP verhindern?

Wenn Benutzereingaben ohne Änderung in eine SQL-Abfrage eingefügt werden, wird die Anwendung anfällig für SQL-Einschleusung , wie im folgenden Beispiel:

$unsafe_variable = $_POST['user_input']; 

mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");

Das liegt daran, dass der Benutzer etwas eingeben kann wie value'); DROP TABLE table;-- und die Abfrage wird:

INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')

Was kann getan werden, um dies zu verhindern?

593voto

Zaffy Punkte 15603

Wie Sie sehen, wird empfohlen, allenfalls vorbereitete Erklärungen zu verwenden. Das ist nicht verkehrt, aber wenn Ihre Abfrage ausgeführt wird nur einmal pro Prozess, würde es einen leichten Leistungsverlust geben.

Ich war mit diesem Problem konfrontiert, aber ich glaube, ich habe es in sehr raffinierte Art und Weise - die Art und Weise, die Hacker verwenden, um die Verwendung von Anführungszeichen zu vermeiden. Ich habe dies in Verbindung mit emulierten vorbereiteten Anweisungen verwendet. Ich benutze es, um zu verhindern alle Arten von möglichen SQL-Injection-Angriffen.

Mein Ansatz:

  • Wenn Sie eine ganzzahlige Eingabe erwarten, vergewissern Sie sich, dass es sich um wirklich Ganzzahlig. In einer Sprache vom Typ Variable wie PHP sieht das so aus sehr wichtig. Sie können zum Beispiel diese sehr einfache, aber leistungsfähige Lösung verwenden: sprintf("SELECT 1,2,3 FROM table WHERE 4 = %u", $input);

  • Wenn Sie etwas anderes von integer erwarten es verhexen . Wenn Sie es verhexen, werden alle Eingaben perfekt unterdrückt. In C/C++ gibt es eine Funktion namens mysql_hex_string() , in PHP können Sie bin2hex() .

    Machen Sie sich keine Sorgen darüber, dass die entkommene Zeichenkette die doppelte Größe ihrer ursprünglichen Länge haben wird, denn selbst wenn Sie mysql_real_escape_string muss PHP die gleiche Kapazität zuweisen. ((2*input_length)+1) was dasselbe ist.

  • Diese Hex-Methode wird häufig bei der Übertragung von Binärdaten verwendet, aber ich sehe keinen Grund, sie nicht für alle Daten zu verwenden, um SQL-Injection-Angriffe zu verhindern. Beachten Sie, dass Sie die Daten mit 0x oder verwenden Sie die MySQL-Funktion UNHEX stattdessen.

So zum Beispiel die Abfrage:

SELECT password FROM users WHERE name = 'root';

Wird werden:

SELECT password FROM users WHERE name = 0x726f6f74;

o

SELECT password FROM users WHERE name = UNHEX('726f6f74');

Hex ist die perfekte Flucht. Keine Möglichkeit zur Injektion.

Unterschied zwischen der Funktion UNHEX und dem Präfix 0x

Es gab einige Diskussionen in den Kommentaren, deshalb möchte ich es endlich klarstellen. Die beiden Ansätze sind sich sehr ähnlich, aber in mancher Hinsicht unterscheiden sie sich doch ein wenig:

El 0x Präfix kann nur für Datenspalten verwendet werden wie char , varchar , text , block , binary , usw.
Außerdem ist seine Verwendung etwas kompliziert, wenn Sie eine leere Zeichenkette einfügen wollen. Dann müssen Sie sie vollständig ersetzen durch '' oder Sie erhalten eine Fehlermeldung.

UNHEX() arbeitet an jede Spalte; Sie müssen sich nicht um die leere Zeichenfolge kümmern.


Verhexungsmethoden werden häufig als Angriffe eingesetzt

Beachten Sie, dass diese Hex-Methode oft für SQL-Injection-Angriffe verwendet wird, bei denen Ganzzahlen wie Zeichenketten behandelt und mit mysql_real_escape_string . Dann können Sie die Verwendung von Anführungszeichen vermeiden.

Wenn Sie zum Beispiel so etwas tun:

"SELECT title FROM article WHERE id = " . mysql_real_escape_string($_GET["id"])

ein Angriff kann Sie sehr einfach . Betrachten Sie den folgenden eingeschleusten Code, der von Ihrem Skript zurückgegeben wird:

SELECT ... WHERE id = -1 UNION ALL SELECT table_name FROM information_schema.tables;

und jetzt einfach die Tabellenstruktur extrahieren:

SELECT ... WHERE id = -1 UNION ALL SELECT column_name FROM information_schema.column WHERE table_name = __0x61727469636c65__;

Und dann wählen Sie einfach die gewünschten Daten aus. Ist das nicht cool?

Aber wenn der Programmierer einer injizierbaren Website es verhexen würde, wäre keine Injektion möglich, weil die Abfrage wie folgt aussehen würde:

SELECT ... WHERE id = UNHEX('2d312075...3635');

0 Stimmen

@Zaffy, ich mag die Idee, aber wie sieht es mit der Leistung aus, ich meine, wenn man 1 Million Datensätze hat und 1000 Benutzer suchen, wird es dann langsamer als bei der vorbereiteten Lösung?

0 Stimmen

Ich teste gerade SELECT * FROM tblproducts WHERE product_code LIKE ( '%42%') findet einen Datensatz, aber SELECT * FROM tblproducts WHERE product_code LIKE ('%' +0x3432 +'%') funktioniert nicht, also funktioniert es einfach nicht oder habe ich etwas falsch gemacht?

9 Stimmen

@SumitGupta Ja, das hast du. MySQL verkettet nicht mit + sondern mit CONCAT . Und zur Leistung: Ich glaube nicht, dass es die Leistung beeinträchtigt, weil mysql Daten parsen muss und es keine Rolle spielt, ob der Ursprung String oder Hex ist.

519voto

rahularyansharma Punkte 11016

Veraltete Warnung: Der Beispielcode dieser Antwort (wie auch der Beispielcode der Frage) verwendet PHPs MySQL Erweiterung, die in PHP 5.5.0 veraltet war und in PHP 7.0.0 vollständig entfernt wurde.

Sicherheitswarnung : Diese Antwort steht nicht im Einklang mit bewährten Sicherheitsverfahren. Escaping ist unzureichend, um SQL-Injection zu verhindern verwenden vorbereitete Erklärungen stattdessen. Die Anwendung der unten beschriebenen Strategie erfolgt auf eigene Gefahr. (Auch, mysql_real_escape_string() wurde in PHP 7 entfernt).

WICHTIG

Der beste Weg, um SQL Injection zu verhindern, ist die Verwendung von Vorbereitete Erklärungen anstatt zu entkommen als die akzeptierte Antwort demonstriert.

Es gibt Bibliotheken wie Aura.Sql y EasyDB die es Entwicklern ermöglichen, vorbereitete Anweisungen einfacher zu verwenden. Um mehr darüber zu erfahren, warum vorbereitete Anweisungen besser sind unter SQL-Injektion stoppen siehe este mysql_real_escape_string() Umgehung y kürzlich behobene Unicode-SQL-Injection-Schwachstellen in WordPress .

Injektionsschutz - mysql_real_escape_string()

PHP verfügt über eine spezielle Funktion, die diese Angriffe verhindert. Alles, was Sie tun müssen, ist, diese Funktion zu verwenden, mysql_real_escape_string .

mysql_real_escape_string nimmt eine Zeichenkette, die in einer MySQL-Abfrage verwendet werden soll, und gibt dieselbe Zeichenkette zurück, wobei alle SQL-Injektionsversuche sicher escaped werden. Im Grunde ersetzt es die lästigen Anführungszeichen('), die ein Benutzer eingeben könnte, durch einen MySQL-sicheren Ersatz, ein escaped quote \'.

HINWEIS: Sie müssen mit der Datenbank verbunden sein, um diese Funktion nutzen zu können!

// Verbindung zu MySQL

$name_bad = "' OR 1'"; 

$name_bad = mysql_real_escape_string($name_bad);

$query_bad = "SELECT * FROM customers WHERE username = '$name_bad'";
echo "Escaped Bad Injection: <br />" . $query_bad . "<br />";

$name_evil = "'; DELETE FROM customers WHERE 1 or username = '"; 

$name_evil = mysql_real_escape_string($name_evil);

$query_evil = "SELECT * FROM customers WHERE username = '$name_evil'";
echo "Escaped Evil Injection: <br />" . $query_evil;

Weitere Einzelheiten finden Sie unter MySQL - Verhinderung von SQL-Injektionen .

33 Stimmen

Das ist das Beste, was Sie mit der Legacy-Mysql-Erweiterung tun können. Für neuen Code sollten Sie zu mysqli oder PDO wechseln.

7 Stimmen

Ich bin nicht einverstanden mit dieser "speziell entwickelten Funktion zur Verhinderung dieser Angriffe". Ich denke, dass mysql_real_escape_string dient dazu, für jeden eingegebenen Datenstring eine korrekte SQL-Abfrage erstellen zu können. Die Verhinderung von SQL-Injection ist der Nebeneffekt dieser Funktion.

5 Stimmen

Sie verwenden keine Funktionen, um korrekte Eingabedatenstrings zu schreiben. Sie schreiben einfach korrekte, die nicht escaped werden müssen oder bereits escaped wurden. mysql_real_escape_string() mag mit dem von Ihnen erwähnten Zweck im Hinterkopf entworfen worden sein, aber ihr einziger Wert ist das Verhindern von Injektionen.

491voto

Tanerax Punkte 5786

Sie könnten etwas Einfaches wie dies tun:

$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");

Das wird nicht alle Probleme lösen, aber es ist ein sehr guter Ausgangspunkt. Ich habe offensichtliche Punkte wie die Überprüfung der Existenz der Variablen, das Format (Zahlen, Buchstaben usw.) ausgelassen.

21 Stimmen

Wenn Sie die Zeichenfolge nicht in Anführungszeichen setzen, ist sie trotzdem injizierbar. Nehmen Sie $q = "SELECT col FROM tbl WHERE x = $safe_var"; zum Beispiel. Einstellung $safe_var a 1 UNION SELECT password FROM users funktioniert in diesem Fall wegen des Fehlens von Anführungszeichen. Es ist auch möglich, Zeichenketten in die Abfrage einzufügen, indem man CONCAT y CHR .

4 Stimmen

@Polynomial Völlig richtig, aber ich würde dies lediglich als falsche Verwendung ansehen. Solange Sie es richtig verwenden, wird es definitiv funktionieren.

0 Stimmen

Wenn ich also diese Codes schreibe, ist die Datenbank immer noch ungeschützt? mysql_query("INSERT INTO table (column) VALUES ('$safe_variable')");

397voto

Rob Punkte 47351

Was auch immer Sie verwenden, stellen Sie sicher, dass Ihre Eingabe nicht bereits durch magic_quotes oder anderen gut gemeinten Blödsinn, und wenn nötig, lassen Sie es durchlaufen stripslashes oder was auch immer, um es zu desinfizieren.

13 Stimmen

In der Tat ermutigt das Einschalten von magic_quotes nur zu schlechten Praktiken. Allerdings kann man die Umgebung nicht immer so gut kontrollieren - entweder hat man keinen Zugang zur Verwaltung des Servers, oder die Anwendung muss mit Anwendungen koexistieren, die (schauder) von einer solchen Konfiguration abhängen. Aus diesen Gründen ist es gut, portable Anwendungen zu schreiben - obwohl der Aufwand natürlich umsonst ist, wenn Sie die Bereitstellungsumgebung kontrollieren können, z. B. weil es sich um eine interne Anwendung handelt oder weil sie nur in Ihrer speziellen Umgebung verwendet werden soll.

29 Stimmen

Seit PHP 5.4 ist die als "magische Anführungszeichen" bekannte Abscheulichkeit totgeschlagen . Und gut, dass wir den schlechten Müll los sind.

378voto

Cedric Punkte 3074

Veraltete Warnung: Der Beispielcode dieser Antwort (wie auch der Beispielcode der Frage) verwendet PHPs MySQL Erweiterung, die in PHP 5.5.0 veraltet war und in PHP 7.0.0 vollständig entfernt wurde.

Sicherheitswarnung : Diese Antwort steht nicht im Einklang mit bewährten Sicherheitsverfahren. Escaping ist unzureichend, um SQL-Injection zu verhindern verwenden vorbereitete Erklärungen stattdessen. Die Anwendung der unten beschriebenen Strategie erfolgt auf eigene Gefahr. (Auch, mysql_real_escape_string() wurde in PHP 7 entfernt).

Parametrisierte Abfragen UND Eingabevalidierung sind der richtige Weg. Es gibt viele Szenarien, in denen SQL-Injection auftreten kann, auch wenn mysql_real_escape_string() verwendet wurde.

Diese Beispiele sind anfällig für SQL-Injection:

$offset = isset($_GET['o']) ? $_GET['o'] : 0;
$offset = mysql_real_escape_string($offset);
RunQuery("SELECT userid, username FROM sql_injection_test LIMIT $offset, 10");

o

$order = isset($_GET['o']) ? $_GET['o'] : 'userid';
$order = mysql_real_escape_string($order);
RunQuery("SELECT userid, username FROM sql_injection_test ORDER BY `$order`");

In beiden Fällen können Sie nicht mit ' um die Verkapselung zu schützen.

Quelle : Die unerwartete SQL-Injektion (wenn die Flucht nicht ausreicht)

3 Stimmen

Sie können eine SQL-Injektion verhindern, wenn Sie eine Eingabevalidierungstechnik anwenden, bei der die Benutzereingabe anhand einer Reihe von definierten Regeln für Länge, Typ und Syntax sowie anhand von Geschäftsregeln authentifiziert wird.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X