SQL-Injection unter Umgehung von mysql_real_escape_string(): Mythen und Realität

Online Python-Trainer für Anfänger

Lernen Sie Python einfach ohne theoretische Überlastung. Lösen Sie praktische Aufgaben mit automatischer Überprüfung und schreiben Sie Code direkt im Browser.

Kurs starten

Einleitung: Sollte man Angst vor mysql_real_escape_string() haben?

\n\n

Sie haben sicherlich den alten Spruch der PHP-Entwickler gehört: „mysql_real_escape_string() schützt vor allen SQL-Injection-Angriffen“. Stimmt das wirklich? Im Jahr 2024, wo PDO und prepared statements zum Standard geworden sind, wirkt dieses Thema archaisch. Aber Tausende von Entwicklern erben noch immer alte Projekte, in denen mysql_real_escape_string() die einzige Verteidigungslinie ist.

\n\n

Auf Stack Overflow hat die Frage „SQL injection that gets around mysql_real_escape_string()“ Hunderttausende von Aufrufen erhalten. Warum? Weil die Antwort nicht offensichtlich ist. In diesem Artikel analysieren wir reale Szenarien, in denen die Maskierung nicht schützt, und vergleichen verschiedene Ansätze zum Schutz. Wenn Sie in PHP programmieren – lesen Sie aufmerksam.

\n\n

Wie funktioniert mysql_real_escape_string()

\n\n

Die Funktion mysql_real_escape_string() (aus der Erweiterung mysql_*, mittlerweile deprecated) maskiert Sonderzeichen in einer Zeichenkette unter Berücksichtigung der aktuellen Verbindungskodierung. Sie fügt einen Backslash vor den Zeichen hinzu: ', ", \\, \\n, \\r, NULL und Control-Z.

\n\n
<?php\n$connection = mysql_connect('localhost', 'user', 'pass');\nmysql_select_db('test', $connection);\n$unsafe = "O'Brien";\n$safe = mysql_real_escape_string($unsafe); // O\\'Brien\n$query = "SELECT * FROM users WHERE name = '$safe'";\n?>
\n\n

Es scheint zu funktionieren. Aber es gibt Nuancen.

\n\n

Szenario 1: Problem mit der Kodierung (GBK / Big5)

\n\n

Die bekannteste Methode zur Umgehung ist die Verwendung von Multibyte-Kodierungen wie GBK (Chinesisch) oder Big5. Wenn die Verbindung auf GBK eingestellt ist und Sie die Zeichenkette %bf%27 übergeben, passiert Magie.

\n\n

In GBK ist das Byte 0xbf kein abgeschlossenes Zeichen. Wenn mysql_real_escape_string() auf ' (0x27) trifft, fügt es einen Backslash 0x5c hinzu. Es entsteht die Sequenz: 0xbf + 0x5c + 0x27. Aber in GBK ist 0xbf5c ein einzelnes gültiges Zeichen (z. B. das Schriftzeichen „縗“). Infolgedessen wird der Backslash „verschluckt“ und das Anführungszeichen bleibt ungeschützt!

\n\n
<?php\n// Kodierung auf GBK setzen\nmysql_query("SET NAMES 'GBK'", $connection);\n$input = "%bf%27"; // Bytes: 0xbf 0x27\n$escaped = mysql_real_escape_string($input, $connection);\n// Ergebnis: 0xbf5c27 -> in GBK ist das "縗'" (Anführungszeichen bleibt)\n$query = "SELECT * FROM users WHERE id = '$escaped'";\n// Tatsächlich ausgeführt: SELECT * FROM users WHERE id = '縗''\n// Jetzt kann injiziert werden: ' OR 1=1 -- \n?>
\n\n

Fazit: Wenn die Kodierung der Verbindung nicht mit der Kodierung der Daten übereinstimmt, kann mysql_real_escape_string() umgangen werden. Genau dieser Fehler wurde im bekannten PHP-Bugreport #47168 beschrieben.

\n\n

Szenario 2: Verwendung numerischer Felder

\n\n

Die Maskierung funktioniert nur mit Zeichenketten. Wenn Sie Daten in ein numerisches Feld ohne Anführungszeichen einfügen, hilft mysql_real_escape_string() nicht, weil der Wert nicht maskiert wird – er wird einfach so eingefügt, wie er ist.

\n\n
<?php\n$id = mysql_real_escape_string($_GET['id']); // Angenommen, "1 OR 1=1"\n$query = "SELECT * FROM users WHERE id = $id"; // Ohne Anführungszeichen!\n// Gefahr: SELECT * FROM users WHERE id = 1 OR 1=1\n?>
\n\n

Selbst wenn Sie die Zeichenkette maskieren, ist dies im numerischen Kontext sinnlos – es gibt keine Anführungszeichen, also kann jeder SQL-Code eingeschleust werden.

\n\n

Fazit: Maskierung ersetzt keine Typprüfung. Für Zahlen verwenden Sie intval() oder ctype_digit().

\n\n

Szenario 3: LIKE-Abfragen und Wildcard-Zeichen

\n\n

mysql_real_escape_string() maskiert ' und \\, berührt aber nicht die Zeichen

Blogs

Buchempfehlungen