Jeśli dane wejściowe użytkownika są wstawiane bez modyfikacji do zapytania SQL, wówczas aplikacja staje się podatna na SQL injection, jak w poniższym przykładzie:
$unsafe_variable = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");
Dzieje się tak dlatego, że użytkownik może wprowadzić coś w rodzaju value'); DROP TABLE table;--
, a zapytanie staje się:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
Co można zrobić, aby temu zapobiec?
Ostrzeżenie o przestarzałej wersji: Przykładowy kod tej odpowiedzi (podobnie jak przykładowy kod pytania) używa rozszerzenia PHP'MySQL`, które zostało wycofane z użycia w PHP 5.5.0 i usunięte całkowicie w PHP 7.0.0.
Ostrzeżenie dotyczące bezpieczeństwa: Ta odpowiedź nie jest zgodna z najlepszymi praktykami bezpieczeństwa. Ucieczka jest niewystarczająca, aby zapobiec SQL injection, zamiast tego używaj prepared statements. Używaj strategii opisanej poniżej na własne ryzyko. (Ponadto,
mysql_real_escape_string()
został usunięty w PHP 7).
Jeśli używasz nowszej wersji PHP, opcja mysql_real_escape_string
nie będzie już dostępna (chociaż mysqli::escape_string
jest jej nowoczesnym odpowiednikiem). W dzisiejszych czasach opcja mysql_real_escape_string
miałaby sens tylko dla starszych wersji PHP.
Masz dwie opcje - ucieczka od znaków specjalnych w unsafe_variable
, lub użycie sparametryzowanego zapytania. Obydwie opcje uchronią Cię przed SQL injection. Zapytanie parametryzowane jest uważane za lepszą praktykę, ale będzie wymagało zmiany na nowsze rozszerzenie MySQL w PHP, zanim będziesz mógł go użyć.
Najpierw zajmiemy się mniejszym wpływem ucieczki od ciągów znaków.
//Connect
$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
//Disconnect
Zobacz także szczegóły funkcji mysql_real_escape_string
.
Aby użyć sparametryzowanego zapytania, musisz użyć MySQLi zamiast funkcji MySQL. Aby przepisać twój przykład, potrzebowalibyśmy czegoś takiego jak poniżej.
<?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();
?>
Kluczową funkcją, o której będziesz chciał poczytać, będzie mysqli::prepare
.
Ponadto, jak sugerowali inni, możesz uznać za użyteczne/łatwiejsze przejście na wyższą warstwę abstrakcji z czymś takim jak PDO.
Należy pamiętać, że przypadek, o który pytałeś, jest dość prosty i że bardziej złożone przypadki mogą wymagać bardziej złożonych podejść. W szczególności:
mysql_real_escape_string
. W tego typu przypadkach, lepiej jest przepuścić dane wejściowe użytkownika przez białą listę, aby upewnić się, że tylko 'bezpieczne' wartości są przepuszczane.mysql_real_escape_string
, będziesz cierpiał z powodu problemu opisanego przez Polynomial w komentarzach poniżej. Ten przypadek jest trudniejszy, ponieważ liczby całkowite nie byłyby otoczone cudzysłowami, więc możesz sobie poradzić, sprawdzając, czy dane wejściowe użytkownika zawierają tylko cyfry.Polecam użycie PDO (PHP Data Objects) do uruchamiania sparametryzowanych zapytań SQL.
Nie tylko chroni to przed SQL injection, ale także przyspiesza wykonywanie zapytań.
A używając PDO zamiast funkcji mysql_
, mysqli_
i pgsql_
, sprawiasz, że Twoja aplikacja jest nieco bardziej oderwana od bazy danych, w rzadkich przypadkach, gdy musisz zmienić dostawcę bazy danych.
Możesz zrobić coś podstawowego, jak to:
$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
Nie rozwiąże to każdego problemu, ale jest to bardzo dobry krok naprzód. Pominąłem oczywiste rzeczy, takie jak sprawdzanie istnienia zmiennej' format (liczby, litery, itp.).