Bien que les systèmes de gestion de base de données (SGBD) qui fonc­tion­nent avec le langage de base de données SQL soient très po­pu­laires, ils ont toujours été sus­cep­tibles d'être manipulés lors de l'in­jec­tion de données. Par exemple, une entrée uti­li­sa­teur qui n'est pas suf­fi­sam­ment masquée et qui contient des mé­ta­ca­rac­tères tels que les guil­le­mets ou les points-virgules est une cible po­ten­tielle pour les at­ta­quants. Une réponse possible à ce problème est l'uti­li­sa­tion des Prepared Sta­te­ments, c'est-à-dire des requêtes préparées à la base de données qui ne sont pas affectées de valeurs avant l'exé­cu­tion.

Qu'est-ce qui rend cette technique si par­ti­cu­lière et où est-elle utilisée ? En utilisant MySQL comme exemple, nous vous montrons comment fonc­tion­nent les requêtes préparées, et comment les utiliser pour la gestion des bases de données.

Que sont les requêtes préparées ?

Les Prepared Sta­te­ments (requêtes préparées) sont des modèles prêts à l'emploi pour les requêtes dans les systèmes de base de données SQL qui ne con­tien­nent pas de valeurs pour les pa­ra­mètres in­di­vi­duels. Ces modèles de dé­cla­ra­tion (également appelés templates de dé­cla­ra­tion) fonc­tion­nent avec des variables ou des ca­rac­tères de rem­plis­sage qui sont uni­que­ment remplacés par les valeurs réelles dans le système con­trai­re­ment à la saisie manuelle, où les valeurs sont déjà at­tri­buées au moment où une commande est exécutée.

Tous les prin­ci­paux systèmes de gestion de bases de données basés sur SQL, tels que MySQL, MariaDB, Oracle, Microsoft SQL Server ou Post­greSQL prennent en charge les requêtes préparées, et la plupart de ces ap­pli­ca­tions utilisent à cette fin un protocole binaire NoSQL. Toutefois, certains systèmes, dont MySQL, utilisent également une syntaxe SQL commune pour la mise en œuvre. En outre, certains langages de pro­gram­ma­tion tels que Java, Perl, Python et PHP sup­por­tent des requêtes par le biais de leurs bi­blio­thèques ou ex­ten­sions standard. Par exemple, si vous utilisez ce dernier langage de script pour l'accès aux bases de données, vous pouvez utiliser soit l'in­ter­face orientée objet PHP Data Objects (PDO), soit l'ex­ten­sion PHP MySQLi pour mettre en œuvre les pre­pa­red­sta­te­ments.

Pourquoi est-il si utile d'uti­li­ser des Prepared Sta­te­ments dans MySQL et autres ?

La raison décisive incitant à tra­vail­ler avec des requêtes préparées dans des systèmes de gestion de bases de données comme MySQL est l'aspect de la sécurité. Le plus gros problème de l'accès standard aux bases de données en langage SQL est pro­ba­ble­ment qu'elles sont fa­ci­le­ment ma­ni­pu­lables. Dans ce cas, on parle d'un SQL-Injection où du code est ajouté ou modifié pour avoir accès à des données sensibles ou même pour avoir un contrôle total sur le système de base de données. Les Prepared Sta­te­ments en PHP ou dans d'autres langues ne pré­sen­tent pas cette faille de sécurité, car seules des valeurs concrètes leur sont at­tri­buées dans le système.

Note

La condition préalable à la norme de sécurité élevée des Prepared Sta­te­ments est qu'aucun élément de cette requête ne soit générée à partir d'une source externe

Toutefois, la pro­tec­tion contre les in­jec­tions SQL n'est pas le seul argument en faveur de l'uti­li­sa­tion de modèles d'ins­truc­tions : une fois analysés et compilés, ils peuvent être utilisés encore et encore par le système de base de données respectif par la suite (avec les valeurs res­pec­tives modifiées). Par con­sé­quent, les requêtes préparées con­som­ment beaucoup moins de res­sources et sont plus rapides que les in­ter­ro­ga­tions manuelles de la base de données lorsque les tâches SQL doivent être exécutées de manière répétée.

Comment fonc­tionne exac­te­ment l'uti­li­sa­tion de pre­pa­red­sta­te­ment ?

Si l'on ne tient pas compte de la syntaxe du langage de script sous-jacent et des par­ti­cu­la­ri­tés du système de gestion de base de données respectif, l'in­té­gra­tion et l'uti­li­sa­tion des requêtes préparées se déroulent gé­né­ra­le­ment dans les phases suivantes :

Phase 1 : Pré­pa­ra­tion des Prepared Sta­te­ments

La première étape consiste à créer un modèle de dé­cla­ra­tion - en PHP par exemple avec la fonction prepare(). Au lieu des valeurs concrètes pour les pa­ra­mètres per­ti­nents, les place holders déjà men­tion­nés sont insérés, qui sont également appelés variables de liaison. En général, ces derniers sont marqués d'un " ? " comme dans l'exemple suivant :

INSERT INTO Produit (Nom, Prix) VALUES (?, ?);

Les requêtes préparées complètes sont ensuite trans­mises au système de gestion de la base de données cor­res­pon­dant.

Phase 2 : trai­te­ment du modèle de pre­pa­red­sta­te­ment par le SGBD

Le système de gestion de la base de données commence par analyser le modèle de dé­cla­ra­tion afin qu'il puisse être compilé à l'étape suivante, c'est-à-dire converti en une dé­cla­ra­tion exé­cu­table. Au cours de ce processus, la requête préparée est également optimisée.

Phase 3 : exécution de la dé­cla­ra­tion préparée

Par la suite, le modèle traité peut être exécuté dans le système de base de données aussi souvent que né­ces­saire. La seule condition préalable à cela est une entrée ap­pro­priée de l'ap­pli­ca­tion connectée ou une source de données qui doit fournir les valeurs cor­res­pon­dantes pour les champs de caractère de rem­plis­sage. Les valeurs "Livre" (nom) et "10" (prix), par exemple, ou "Or­di­na­teur" et "1000" pour­raient cor­res­pondre à l'exemple de code ci-dessus (phase 1).

Tutoriel : comment utiliser les Prepared Sta­te­ments dans MySQL avec MySQLi

Après avoir décrit la fonc­tion­na­lité des requêtes préparées en général, le tutoriel suivant ex­pli­quera l'uti­li­sa­tion des ins­truc­tions pratiques à l'aide d'exemples concrets. Le tutoriel est basé sur les exemples suivants :

  • MySQL comme système de gestion de base de données et
  • PHP comme langage de requête préparée.

Les versions actuelles de MySQL prennent en charge l'uti­li­sa­tion côté serveur de requêtes préparées basées sur un protocole binaire qui contient toutes les commandes SQL, mettant à jour les données, et en­re­gistre également toutes les mises à jour depuis la dernière sau­ve­garde. Dans ce tutoriel, l'ex­ten­sion PHP MySQLi est utilisée comme interface d'accès, qui prend également en charge les Prepared Sta­te­ments basées sur le protocole binaire.

Note

Une bonne al­ter­na­tive à MySQLi, fré­quem­ment utilisée comme API de requête préparée, est l‘interface orientée objet PDO (PHP Data Objects). Cette variante est gé­né­ra­le­ment con­si­dé­rée comme une solution plus facile d‘uti­li­sa­tion pour les débutants

PREPARE, EXECUTE et DEAL­LO­CATE PREPARE : les trois commandes SQL élé­men­taires pour utiliser les Prepared Sta­te­ments

Trois commandes SQL jouent un rôle essentiel dans l'ap­pli­ca­tion des requêtes préparées dans les bases de données MySQL :

La commande "PREPARE" est né­ces­saire pour préparer une requête préparée à l'usage et, entre autres, pour lui attribuer un nom unique sous lequel la requête peut être consultée ul­té­rieu­re­ment.

PREPARE stmt_name FROM preparable_stmt

Pour exécuter des requêtes SQL préparées, vous avez besoin de la commande "EXECUTE". Vous vous référez à la requête préparée res­pec­tive en spé­ci­fiant le nom généré avec "PREPARE". La fréquence d'exé­cu­tion d'une dé­cla­ra­tion dépend de vous : vous pouvez définir un nombre quel­conque de variables dif­fé­rentes ou trans­mettre un nombre illimité de nouvelles valeurs pour les variables définies.

EXECUTE stmt_name
	[USING @var_name [, @var_name] ...]

Pour libérer une Prepared Statement PHP, utilisez la requête "DEAL­LO­CATE PREPARE". Par ailleurs, les requêtes sont au­to­ma­ti­que­ment publiées à la fin d'une session. La li­bé­ra­tion est per­ti­nente dans la mesure où, sinon, la limite su­pé­rieure spécifiée par la variable système max_prepared_stmt_count est ra­pi­de­ment atteinte. Vous ne pourriez alors pas générer de nouvelles requêtes préparées.

{DEALLOCATE | DROP} PREPARE stmt_name

Vous pouvez utiliser ces requêtes SQL comme des Prepared Sta­te­ments MySQL

Vous pouvez préparer et exécuter presque toutes les requêtes SQL prises en charge par MySQL en tant qu'ins­truc­tions préparées. La seule exception à cette règle concerne les requêtes de diag­nos­tic, qui sont exclues en tant que requêtes préparées pour se conformer à la norme SQL. Il s'agit donc des requêtes suivantes :

  • SHOW WARNINGS
  • SHOW COUNT(*) WARNINGS
  • SHOW ERRORS
  • SHOW COUNT(*) ERRORS

En outre, vous ne pouvez pas générer de modèles pour les requêtes SQL en référence aux variables système warning_count et error_count.

Vous pouvez toutefois utiliser les pre­pa­red­sta­te­ments suivants :

ALTER TABLE
ALTER USER
ANALYZE TABLE
CACHE INDEX
CALL
CHANGE MASTER
CHECKSUM {TABLE | TABLES}
COMMIT
{CREATE | DROP} INDEX
{CREATE | RENAME | DROP} DATABASE
{CREATE | DROP} TABLE
{CREATE | RENAME | DROP} USER
{CREATE | DROP} VIEW
DELETE
DO
FLUSH {TABLE | TABLES | TABLES WITH READ LOCK | HOSTS | PRIVILEGES
	| LOGS | STATUS | MASTER | SLAVE | USER_RESOURCES}
GRANT
INSERT
INSTALL PLUGIN
KILL
LOAD INDEX INTO CACHE
OPTIMIZE TABLE
RENAME TABLE
REPAIR TABLE
REPLACE
RESET {MASTER | SLAVE}
REVOKE
SELECT
SET
SHOW {WARNINGS | ERRORS}
SHOW BINLOG EVENTS
SHOW CREATE {PROCEDURE | FUNCTION | EVENT | TABLE | VIEW}
SHOW {MASTER | BINARY} LOGS
SHOW {MASTER | SLAVE} STATUS
SLAVE {START | STOP}
TRUNCATE TABLE
UNINSTALL PLUGIN
UPDATE

Par­ti­cu­la­ri­tés syn­taxiques de la syntaxe SQL des Prepared Sta­te­ments en PHP

Par rapport à la syntaxe SQL standard, la syntaxe des requêtes préparées présente quelques par­ti­cu­la­ri­tés qui doivent être res­pec­tées. Tout d'abord, l'uti­li­sa­tion de place holders pour les valeurs des pa­ra­mètres rend les requêtes préparées in­té­res­santes pour l'accès aux systèmes de gestion de bases de données. À partir de MySQL 8.0, par exemple, de tels place holders sont également possibles pour les pa­ra­mètres "OUT" et "INOUT" dans les requêtes "PREPARE" et "EXECUTE" (pour les pa­ra­mètres "IN", ils sont même dis­po­nibles quelle que soit la version du système de base de données). Les autres pro­prié­tés spé­ci­fiques de la syntaxe de la requête préparée sont les suivantes :

  • La syntaxe SQL des requêtes préparées en PHP ne peut pas être imbriquée. Une requête transmise à une requête "PREPARE" ne peut pas être elle-même une requête "PREPARE", "EXECUTE" ou "DEAL­LO­CATE PREPARE".
  • Les Prepared Sta­te­ments peuvent être utilisées dans des pro­cé­dures stockées (fonction d'appel de séquences complètes de requêtes).
  • Les requêtes multiples, également appelées multi-sta­te­ments, ne sont pas possibles à l'in­té­rieur d'une requête préparée ou à l'in­té­rieur d'une chaîne en les séparant par un point-virgule.

Requêtes préparées en MySQLi : exemple

Cet exemple montre exac­te­ment à quoi ressemble une entrée avec des Prepared Sta­te­ments PHP dans MySQLi :

<?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("Connection failed: " . $conn->connect_error);
}
// Préparation des Prepared Statements
$stmt = $conn->prepare("INSERT INTO Mesclients (Prénom, Nom, Mail) VALUES (?, ?, ?)");
$stmt->bind_param("sss", $Prénom, $Nom, $mail);
// Paramétrage et exécution
$Prénom = "Max";
$Nom = "Dupont";
$Mail = "max@exemple.fr";
$stmt->execute();
$Prénom = "Sophie";
$Nom = "Dupuis";
$Mail = "sophie@exemple.fr";
$stmt->execute();
$Prénom = "Eric";
$Nom = "Delcroix";
$Mail = "eric@exemple.fr";
$stmt->execute();
echo "Nouvelles entrées créées avec succès";
$stmt->close();
$conn->close();
?>

Ce script PHP établit d'abord la connexion à la base de données MySQL ($conn), où les données in­di­vi­duelles du serveur telles que le nom d'hôte, le nom d'uti­li­sa­teur, le mot de passe et le nom de la base de données doivent être spé­ci­fiées.

Avec la ligne "INSERT INTO Mes­clients (Prénom, Nom, Mail) VALUES ( ?, ?, ?)", la partie décisive de la requête préparée commence : la base de données clients "Mes­clients" doit recevoir la saisie (INSERT INTO) pour les colonnes "Prénom", "Nom" et "Mail" res­pec­ti­ve­ment. Pour les valeurs (VALUES), on utilise d'abord des ca­rac­tères de rem­plis­sage, qui sont iden­ti­fiés par le symbole du point d'in­ter­ro­ga­tion ( ?).

Ensuite, les pa­ra­mètres doivent être liés (bind_param). La base de données a également besoin d'in­for­ma­tions sur le type de données con­cer­nées. Par exemple, l'ar­gu­ment "sss" utilisé ici indique que les trois pa­ra­mètres sont des chaînes de ca­rac­tères. Les al­ter­na­tives possibles se situent à ce stade :

  • i: INTEGER (valeur entière)
  • d: DOUBLE (valeur numérique ap­proxi­ma­tive de la donnée)
  • b: BLOB (grand objet de données binaires)
aN5KqxK1slc.jpg Pour afficher cette vidéo, des cookies de tiers sont nécessaires. Vous pouvez consulter et modifier vos paramètres de cookies ici.
Aller au menu principal