Dichiarazioni preparate per PHP MySQL
Le istruzioni preparate sono molto utili contro le iniezioni SQL.
Dichiarazioni preparate e parametri vincolati
Un'istruzione preparata è una funzionalità utilizzata per eseguire ripetutamente le stesse (o simili) istruzioni SQL con elevata efficienza.
Le dichiarazioni preparate funzionano fondamentalmente in questo modo:
- Preparazione: un modello di istruzione SQL viene creato e inviato al database. Alcuni valori non vengono specificati, chiamati parametri (etichettati con "?"). Esempio: INSERT INTO MyGuests VALUES(?, ?, ?)
- Il database analizza, compila ed esegue l'ottimizzazione della query sul modello di istruzione SQL e archivia il risultato senza eseguirlo
- Esegui: in un secondo momento, l'applicazione associa i valori ai parametri e il database esegue l'istruzione. L'applicazione può eseguire l'istruzione tutte le volte che vuole con valori diversi
Rispetto all'esecuzione diretta di istruzioni SQL, le istruzioni preparate presentano tre vantaggi principali:
- Le istruzioni preparate riducono il tempo di analisi poiché la preparazione della query viene eseguita solo una volta (sebbene l'istruzione venga eseguita più volte)
- I parametri associati riducono al minimo la larghezza di banda al server poiché è necessario inviare solo i parametri ogni volta e non l'intera query
- Le istruzioni preparate sono molto utili contro le iniezioni SQL, poiché non è necessario eseguire correttamente l'escape dei valori dei parametri, che vengono trasmessi in seguito utilizzando un protocollo diverso. Se il modello di istruzione originale non è derivato da input esterno, non può essere eseguita l'iniezione SQL.
Dichiarazioni preparate in MySQLi
L'esempio seguente utilizza istruzioni preparate e parametri associati in MySQLi:
Esempio (MySQLi con istruzioni preparate)
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// Create connection
$conn = new mysqli($servername, $username, $password, $dbname);
// Check connection
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
// prepare and bind
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);
// set parameters and execute
$firstname = "John";
$lastname = "Doe";
$email = "[email protected]";
$stmt->execute();
$firstname = "Mary";
$lastname = "Moe";
$email = "[email protected]";
$stmt->execute();
$firstname = "Julie";
$lastname = "Dooley";
$email = "[email protected]";
$stmt->execute();
echo "New records created successfully";
$stmt->close();
$conn->close();
?>
Righe di codice per spiegare dall'esempio sopra:
"INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)"
Nel nostro SQL, inseriamo un punto interrogativo (?) dove vogliamo sostituire un valore intero, stringa, double o blob.
Quindi, dai un'occhiata alla funzione bind_param():
$stmt->bind_param("sss", $firstname, $lastname, $email);
Questa funzione associa i parametri alla query SQL e dice al database quali sono i parametri. L'argomento "sss" elenca i tipi di dati che sono i parametri. Il carattere s dice a MySQL che il parametro è una stringa.
L'argomento può essere di quattro tipi:
- i - intero
- d - doppio
- s - stringa
- b - BLOB
Dobbiamo avere uno di questi per ogni parametro.
Dicendo a MySQL quale tipo di dati aspettarsi, riduciamo al minimo il rischio di iniezioni SQL.
Nota: se vogliamo inserire dati da fonti esterne (come l'input dell'utente), è molto importante che i dati siano disinfettati e convalidati.
Dichiarazioni Preparate in DOP
L'esempio seguente utilizza istruzioni preparate e parametri associati in PDO:
Esempio (DOP con dichiarazioni preparate)
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDBPDO";
try {
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
// set the PDO error mode to exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// prepare sql and bind parameters
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email)
VALUES (:firstname, :lastname, :email)");
$stmt->bindParam(':firstname', $firstname);
$stmt->bindParam(':lastname', $lastname);
$stmt->bindParam(':email', $email);
// insert a row
$firstname = "John";
$lastname = "Doe";
$email = "[email protected]";
$stmt->execute();
// insert another row
$firstname = "Mary";
$lastname = "Moe";
$email = "[email protected]";
$stmt->execute();
// insert another row
$firstname = "Julie";
$lastname = "Dooley";
$email = "[email protected]";
$stmt->execute();
echo "New records created successfully";
} catch(PDOException $e)
{
echo "Error: " . $e->getMessage();
}
$conn = null;
?>