Hvis brukerinput settes inn uten endring i en SQL-spørring, blir applikasjonen sårbar for SQL-injeksjon, som i følgende eksempel:
$unsafe_variable = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");
Det' er fordi brukeren kan legge inn noe som verdi'); DROP TABLE-tabell;--
, og spørringen blir:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
Hva kan gjøres for å forhindre at dette skjer?
Utdatert advarsel:. Dette svarets eksempelkode (som spørsmålets eksempelkode) bruker PHPs
MySQL
-utvidelse, som ble utdatert i PHP 5.5.0 og fjernet helt i PHP 7.0.0.
Sikkerhetsadvarsel: Dette svaret er ikke i tråd med beste praksis for sikkerhet. Escaping er utilstrekkelig for å forhindre SQL-injeksjon, bruk prepared statements i stedet. Bruk strategien beskrevet nedenfor på eget ansvar. (Dessuten ble
mysql_real_escape_string()
fjernet i PHP 7).
Hvis du bruker en nyere versjon av PHP, vil mysql_real_escape_string
-alternativet beskrevet nedenfor ikke lenger være tilgjengelig (selv om mysqli::escape_string
er en moderne ekvivalent). I disse dager vil mysql_real_escape_string
-alternativet bare gi mening for eldre kode på en gammel versjon av PHP.
Du har to alternativer - å unnslippe spesialtegnene i unsafe_variable
, eller bruke en parametrisert spørring. Begge vil beskytte deg mot SQL-injeksjon. Den parametriserte spørringen anses som den beste praksisen, men vil kreve endring til en nyere MySQL-utvidelse i PHP før du kan bruke den.
Vi' vil dekke den nedre effektstrengen som rømmer en først.
//Connect
$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
//Disconnect
Se også detaljene for funksjonen mysql_real_escape_string
.
For å bruke den parametriserte spørringen må du bruke MySQLi i stedet for MySQL-funksjonene. For å omskrive eksemplet ditt, trenger vi noe som følgende.
<?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();
?>
Nøkkelfunksjonen du ønsker å lese om der vil være mysqli::prepare
.
Som andre har foreslått, kan det også være nyttig/enklere å gå opp et abstraksjonsnivå med noe som PDO.
Vær oppmerksom på at saken du spurte om, er ganske enkel, og at mer komplekse saker kan kreve mer komplekse tilnærminger. Dette gjelder særlig
mysql_real_escape_string
. I slike tilfeller er det bedre å sende brukerens input gjennom en hviteliste for å sikre at bare "sikre" verdier slipper gjennom.mysql_real_escape_string
-tilnærmingen, vil du lide av problemet beskrevet av Polynom i kommentarene nedenfor. Dette tilfellet er vanskeligere fordi heltall ikke vil være omgitt av anførselstegn, slik at du kan håndtere ved å validere at brukerinngangen bare inneholder sifre.Jeg anbefaler å bruke PDO (PHP Data Objects) for å kjøre parametriserte SQL-spørringer.
Ikke bare beskytter dette mot SQL-injeksjon, men det gir også raskere spørringer.
Og ved å bruke PDO i stedet for mysql_
, mysqli_
og pgsql_
funksjoner, gjør du applikasjonen din litt mer abstrahert fra databasen, i det sjeldne tilfellet at du må bytte databaseleverandør.
Du kan gjøre noe grunnleggende som dette:
$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
Dette vil ikke løse alle problemer, men det er et veldig godt springbrett. Jeg utelot åpenbare elementer som å sjekke variabelens eksistens, format (tall, bokstaver osv.).