PHP Instructions préparées MySQL
Les instructions préparées sont très utiles pour se protéger contre les injections SQL.
Instructions préparées et paramètres liés
Une instruction préparée est une fonctionnalité qui permet d'exécuter les mêmes (ou des instructions SQL similaires) de manière répétée avec une grande efficacité.
Les instructions préparées fonctionnent essentiellement de la manière suivante :
- 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 optimise la requête sur le modèle d'instruction SQL, et stocke le résultat sans l'exécuter.
- Exécuter : À un moment ultérieur, 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.
Comparées à l'exécution directe des instructions SQL, les instructions préparées présentent trois principaux avantages :
- Elles réduisent le temps d'analyse, car la préparation de la requête n'est effectuée qu'une seule fois (même si l'instruction est 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.
- Elles sont très utiles pour se protéger contre les injections SQL, car les valeurs des paramètres, qui sont transmises plus tard en utilisant un protocole différent, n'ont pas besoin d'être correctement échappées. Si le modèle d'instruction original 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 instructions préparées)
<?php
$servername = "localhost";
$username = "username";
$password = "password";
$dbname = "myDB";
// Créer une connexion
$conn = new mysqli($servername, $username, $password, $dbname);
// Vérifier la connexion
if ($conn->connect_error) {
die("Échec de la connexion : " . $conn->connect_error);
}
// Préparer et lier
$stmt = $conn->prepare("INSERT INTO MyGuests (firstname, lastname, email) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $firstname, $lastname, $email);
// Définir les paramètres et exécuter
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();
$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();
$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();
echo "Nouveaux enregistrements créés avec succès";
$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 (?) là où nous voulons substituer une valeur entière, chaîne, double ou blob.
Ensuite, examinons 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" énumère les types de données que représentent 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 :
- i - entier
- d - double
- s - chaîne
- b - BLOB
Nous devons avoir l'un de ces types 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 une entrée utilisateur), il est très important que ces données soient assainies et validées.
Instructions 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);
// Définir le mode d'erreur PDO sur exception
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
// Préparer SQL et lier les paramètres
$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);
// Insérer une ligne
$firstname = "John";
$lastname = "Doe";
$email = "john@example.com";
$stmt->execute();
// Insérer une autre ligne
$firstname = "Mary";
$lastname = "Moe";
$email = "mary@example.com";
$stmt->execute();
// Insérer une autre ligne
$firstname = "Julie";
$lastname = "Dooley";
$email = "julie@example.com";
$stmt->execute();
echo "Nouveaux enregistrements créés avec succès";
} catch(PDOException $e) {
echo "Erreur : " . $e->getMessage();
}
$conn = null;
?>