Instructions préparées PHP MySQL
Les instructions préparées sont très utiles contre les injections SQL.
Instructions préparées et paramètres liés
Une instruction préparée est une fonctionnalité utilisée pour exécuter les mêmes instructions SQL (ou similaires) à plusieurs reprises avec une grande efficacité.
Les instructions préparées fonctionnent essentiellement comme ceci :
- Préparer : un modèle d'instruction SQL est créé et envoyé à la base de données. Certaines valeurs sont laissées non spécifiées, appelées paramètres (étiquetés "?"). Exemple : INSERT INTO MyGuests VALUES(?, ?, ?)
- La base de données analyse, compile et effectue l'optimisation des requêtes sur le modèle d'instruction SQL et stocke le résultat sans l'exécuter
- Exécuter : ultérieurement, l'application lie les valeurs aux paramètres et la base de données exécute l'instruction. L'application peut exécuter l'instruction autant de fois qu'elle le souhaite avec des valeurs différentes
Par rapport à l'exécution directe d'instructions SQL, les instructions préparées présentent trois avantages principaux :
- Les instructions préparées réduisent le temps d'analyse car la préparation de la requête n'est effectuée qu'une seule fois (bien que l'instruction soit exécutée plusieurs fois)
- Les paramètres liés minimisent la bande passante vers le serveur car vous n'avez besoin d'envoyer que les paramètres à chaque fois, et non la requête entière
- Les instructions préparées sont très utiles contre les injections SQL, car les valeurs des paramètres, qui sont transmises ultérieurement à l'aide d'un protocole différent, n'ont pas besoin d'être correctement échappées. Si le modèle d'instruction d'origine n'est pas dérivé d'une entrée externe, l'injection SQL ne peut pas se produire.
Instructions préparées dans MySQLi
L'exemple suivant utilise des instructions préparées et des paramètres liés dans MySQLi :
Exemple (MySQLi avec des instructions préparées)
<?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();
?>
Lignes de code à expliquer à partir de l'exemple ci-dessus :
"INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)"
Dans notre SQL, nous insérons un point d'interrogation (?) où nous voulons substituer une valeur entière, chaîne, double ou blob.
Ensuite, jetez un œil à la fonction bind_param() :
$stmt->bind_param("sss", $firstname, $lastname, $email);
Cette fonction lie les paramètres à la requête SQL et indique à la base de données quels sont les paramètres. L'argument "sss" répertorie les types de données que sont les paramètres. Le caractère s indique à mysql que le paramètre est une chaîne.
L'argument peut être l'un des quatre types suivants :
- je - entier
- d - double
- s - chaîne
- b - BLOB
Nous devons en avoir un pour chaque paramètre.
En indiquant à mysql quel type de données attendre, nous minimisons le risque d'injections SQL.
Remarque : Si nous voulons insérer des données provenant de sources externes (comme l'entrée de l'utilisateur), il est très important que les données soient nettoyées et validées.
Déclarations préparées dans PDO
L'exemple suivant utilise des instructions préparées et des paramètres liés dans PDO :
Exemple (PDO avec instructions préparées)
<?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;
?>