Jos käyttäjän syötteet lisätään muokkaamattomina SQL-kyselyyn, sovelluksesta tulee altis SQL-injektiolle, kuten seuraavassa esimerkissä:
$unsafe_variable = $_POST['user_input'];
mysql_query("INSERT INTO `table` (`column`) VALUES ('$unsafe_variable')");
Tämä johtuu siitä, että käyttäjä voi syöttää jotain sellaista kuin value'); DROP TABLE table;--
, ja kyselystä tulee:
INSERT INTO `table` (`column`) VALUES('value'); DROP TABLE table;--')
Mitä voidaan tehdä tämän estämiseksi?
Poistettu varoitus: Tämän vastauksen esimerkkikoodi (kuten myös kysymyksen esimerkkikoodi) käyttää PHP:n
MySQL
-laajennusta, joka poistettiin käytöstä PHP 5.5.0:ssa ja poistettiin kokonaan PHP 7.0.0:ssa.
Turvallisuusvaroitus: Tämä vastaus ei ole parhaiden turvallisuuskäytäntöjen mukainen. Escaping is inquate to prevent SQL injection, käytä sen sijaan prepared statements. Käytä alla esitettyä strategiaa omalla vastuullasi. (Lisäksi
mysql_real_escape_string()
poistettiin PHP 7:stä.)
Jos käytät uudempaa PHP-versiota, alla kuvattu mysql_real_escape_string
-vaihtoehto ei ole enää käytettävissä (vaikka mysqqli::escape_string
on nykyaikainen vastine). Nykyään mysql_real_escape_string
-vaihtoehto on järkevä vain vanhassa PHP-versiossa käytettävälle koodille.
Sinulla on kaksi vaihtoehtoa - erikoismerkkien välttäminen unsafe_variable
-muodossa tai parametrisoidun kyselyn käyttäminen. Molemmat suojaisivat sinua SQL-injektiolta. Parametrisoitua kyselyä pidetään parempana käytäntönä, mutta se vaatii vaihtamista uudempaan MySQL-laajennukseen PHP:ssä ennen kuin voit käyttää sitä.
Käsittelemme ensin vähemmän vaikuttavaa merkkijonon escaping-menetelmää.
//Connect
$unsafe_variable = $_POST["user-input"];
$safe_variable = mysql_real_escape_string($unsafe_variable);
mysql_query("INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
//Disconnect
Katso myös mysql_real_escape_string
-funktion yksityiskohdat.
Jos haluat käyttää parametrisoitua kyselyä, sinun on käytettävä MySQLi eikä MySQL-funktioita. Kirjoittaaksemme esimerkkisi uudelleen, tarvitsisimme jotakin seuraavan kaltaista.
<?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();
?>
Avainfunktio, josta sinun kannattaa lukea lisää, on mysqli::prepare
.
Lisäksi, kuten muut ovat ehdottaneet, saatat pitää hyödyllisenä/helpompana nostaa abstraktiokerrosta jonkinlaisella PDO -ohjelmalla.
Huomaa, että kysymäsi tapaus on melko yksinkertainen ja että monimutkaisemmat tapaukset saattavat vaatia monimutkaisempia lähestymistapoja. Erityisesti:
mysql_real_escape_string
-ohjelma kata vaadittavaa escaping-toimintoa. Tällaisessa tapauksessa käyttäjän syötteet olisi parempi siirtää valkoisen listan kautta, jotta varmistetaan, että vain 'turvalliset' arvot pääsevät läpi.mysql_real_escape_string
-menetelmää, kärsit ongelmasta, jonka Polynomial kuvasi alla olevissa kommenteissa. Tämä tapaus on hankalampi, koska kokonaislukuja ei ympäröisi lainausmerkit, joten voit käsitellä sitä varmistamalla, että käyttäjän syöttö sisältää vain numeroita.Suosittelen käyttämään PDO (PHP Data Objects) -ohjelmaa parametrisoitujen SQL-kyselyjen suorittamiseen.
Tämä ei ainoastaan suojaa SQL-injektioita vastaan, vaan myös nopeuttaa kyselyjä.
Käyttämällä PDO:ta mysql_
, mysqli_
ja pgsql_
-funktioiden sijasta teet sovelluksestasi hieman abstraktimman tietokannan suhteen siinä harvinaisessa tapauksessa, että joudut vaihtamaan tietokantatoimittajaa.
Voisit tehdä jotain tällaista:
$safe_variable = mysqli_real_escape_string($_POST["user-input"], $dbConnection);
mysqli_query($dbConnection, "INSERT INTO table (column) VALUES ('" . $safe_variable . "')");
Tämä ei ratkaise jokaista ongelmaa, mutta se on erittäin hyvä ponnahduslauta. Jätin pois ilmeiset asiat, kuten muuttujan olemassaolon tarkistamisen, muodon (numerot, kirjaimet jne.).