Wenn Benutzereingaben ohne Änderung in eine SQL-Abfrage eingefügt werden, wird die Anwendung anfällig für SQL-Injection, 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 wie value'); DROP TABLE table;--
eingeben kann, und die Abfrage wird:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
Was kann man tun, um dies zu verhindern?
Veraltete Warnung: Der Beispielcode dieser Antwort (wie auch der Beispielcode der Frage) verwendet die PHP-Erweiterung
MySQL
, die in PHP 5.5.0 veraltet war und in PHP 7.0.0 vollständig entfernt wurde.
Sicherheitswarnung: Diese Antwort entspricht nicht den besten Sicherheitspraktiken. [Escaping ist unzureichend, um SQL-Injection zu verhindern (https://paragonie.com/blog/2015/05/preventing-sql-injection-in-php-applications-easy-and-definitive-guide), verwenden Sie stattdessen prepared statements. Verwenden Sie die unten beschriebene Strategie auf eigenes Risiko. (Außerdem wurde
mysql_real_escape_string()
in PHP 7 entfernt).
Wenn Sie eine neuere Version von PHP benutzen, wird die unten beschriebene Option mysql_real_escape_string
nicht mehr verfügbar sein (obwohl mysqli::escape_string
ein modernes Äquivalent ist). Heutzutage würde die Option mysql_real_escape_string
nur noch für Legacy-Code auf einer alten PHP-Version Sinn machen.
Sie haben zwei Möglichkeiten - die Sonderzeichen in Ihrer unsafe_variable
zu escapen, oder eine parametrisierte Abfrage zu verwenden. Beides würde Sie vor SQL-Injection schützen. Die parametrisierte Abfrage gilt als die bessere Methode, erfordert aber den Wechsel zu einer neueren MySQL-Erweiterung in PHP, bevor Sie sie verwenden können.
Wir befassen uns zuerst mit der weniger folgenschweren String-Escaping-Methode.
//Connect
$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
//Disconnect
Siehe auch die Details der Funktion mysql_real_escape_string
.
Um die parametrisierte Abfrage zu verwenden, müssen Sie MySQLi und nicht die MySQL-Funktionen verwenden. Um Ihr Beispiel umzuschreiben, bräuchten wir etwas wie das Folgende.
<?php
$mysqli = new mysqli("server", "username", "password", "database_name");
// TODO - Check that connection was successful.
$unsafe_variable = $_POST["user-input"];
$stmt = $mysqli->prepare("INSERT INTO table (column) VALUES (?)");
// TODO check that $stmt creation succeeded
// "s" means the database expects a string
$stmt->bind_param("s", $unsafe_variable);
$stmt->execute();
$stmt->close();
$mysqli->close();
?>
Die Schlüsselfunktion, über die Sie sich informieren sollten, wäre mysqli::prepare
.
Wie bereits von anderen vorgeschlagen, könnten Sie es auch nützlich/einfacher finden, eine Abstraktionsebene höher zu gehen, z.B. mit PDO.
Bitte beachten Sie, dass der Fall, nach dem Sie gefragt haben, ein recht einfacher ist und dass komplexere Fälle möglicherweise komplexere Ansätze erfordern. Im Besonderen:
mysql_real_escape_string
nicht abgedeckt. In solchen Fällen ist es besser, die Benutzereingabe durch eine Whitelist zu leiten, um sicherzustellen, dass nur 'sichere' Werte durchgelassen werden.mysql_real_escape_string
Ansatz wählen, werden Sie unter dem Problem leiden, das von Polynomial in den Kommentaren unten beschrieben wird. Dieser Fall ist kniffliger, weil Ganzzahlen nicht von Anführungszeichen umgeben sind, so dass Sie damit umgehen können, indem Sie überprüfen, ob die Benutzereingabe nur Ziffern enthält.Ich empfehle die Verwendung von PDO (PHP Data Objects), um parametrisierte SQL-Abfragen durchzuführen.
Dies schützt nicht nur vor SQL-Injection, sondern beschleunigt auch die Abfragen.
Durch die Verwendung von PDO anstelle von mysql_
-, mysqli_
- und pgsql_
-Funktionen abstrahieren Sie Ihre Anwendung ein wenig von der Datenbank, für den seltenen Fall, dass Sie den Datenbankanbieter wechseln müssen.
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 jedes Problem lösen, aber es ist ein guter Anfang. Ich habe offensichtliche Dinge wie die Überprüfung der Existenz der Variablen und das Format (Zahlen, Buchstaben usw.) weggelassen.