Cours requêtes préparées
1. Introduction :
L'utilisation d'une base de données depuis une application soulève deux problèmes essentiels :
La vitesse d'exécution de la requête sur le SGBD, principal goulet d'étranglement de
l'application ;
Le problème de sécurité à cause du risque d'injection de code SQL par l'utilisateur.
2. Problématique
a. Vitesse d'exécution
Certaines requêtes peuvent s'avérer coûteuses en temps d'exécution.
L'utilisation répétitive fait chuter les performances de l'application. Or il est fréquent de répéter la
même requête en faisant juste varier quelques paramètres dans celle-ci comme vous pouvez le
constatez sur l’exemple ci-dessous :
SELECT nom,prenom FROM personne where id=12 ;
est semblable à
SELECT nom,prenom FROM personne where id=40 ;
b. Injection SQL
Il s'agit d'une faille classique. Elle consiste à détourner une requête en la transformant.
Considérons la technique qui peut être utilisée pour authentifier un utilisateur dans une
application :
L'utilisateur entre son login et mot de passe ;
L'application recherche ensuite dans la table des utilisateurs s'il existe une entrée
comprenant à la fois le login et le mot de passe fournis.
Si c'est le cas, alors un enregistrement est retourné, donc l'authentification de l'utilisateur est
correcte
Vous allez réaliser un programme qui affiche un formulaire permettant de saisir un login et mot de
passe, puis affiche le numéro personnel de cet utilisateur.
Créez sous phpmyadmin, une base de données "testinjection" puis exécutez le script utilisateur.sql
Enregistrez ensuite les fichiers formulaireTestInjection.php et AfficheTel.php depuis Moodle dans
www
Vérifier que la connexion à votre bdd est correctement configurée dans le fichier
AffichageTelMdp.php
Testez le bon fonctionnement de l’application.
Supposons qu’un utilisateur entre un login inconnu comme "Dupont" et "bof" pour le mot de passe,
La requête : SELECT * FROM Utilisateurs
WHERE login="dupont" et mdp="bof";
Donnera une page vide puisque l’utilisateur Dupont n’existe pas ou ce mot de passe n’est pas valide
Et bien il est simple de détourner cette requête pour qu'elle retourne une ligne valide, par exemple
en écrivant :
SELECT * FROM utilisateurs WHERE login='dupont' AND mdp='bof' OR 'a'='a' ;
il suffit d’entrer la chaîne suivante à la place du mot de passe : bof’ OR ‘a’=‘a
a=a sera toujours vrai, il obtiendra
la liste des logins et mot de passe,
plus les numéros de téléphone
3. Requêtes paramétrées
Dans la requête, les données devant être remplacées seront écrites en mettant un point
d'interrogation à la place d'une valeur.
SELECT nom, prenom from personnes where id= ?;
Il est également possible de nommer les paramètres plutôt que d'utiliser des pojnts d'interrogation :
SELECT nom, prenom from personnes where id=:param1;
On utilise la méthode prepare() de l'objet de connexion PDO pour envoyer la requête :
$prep = $pdo->prepare('requête');
Un objet PDOStatement est créé.
Ensuite on doit indiquer une valeur pour chaque paramètre, grâce à la méthode bindParam() de
l'objet PDOStatement précédemment créé.
$prep->bindParam(n, valeur) ;
le paramètre n vaut un pour le premier ? de la requête, 2 pour le deuxième, etc…
Si l'on a nommé les paramètres, alors on écrit :
$prep->bindParam(':param1', valeur) ;
Remarque : dans tous les cas, la valeur passée peut être issue d'une variable.
On peut ensuite exécuter la requête :
$prep->execute( ) ;
http://php.net/manual/fr/pdostatement.execute.php pour d’autres méthodes
Exemple :
$req="SELECT id,prenom FROM ma_table WHERE nom= ? AND age< ?" ;
$prep = $connex->prepare($req) ;
$age=44 ;
$prep->bindParam(1,'Dalton') ;
$prep->bindParam(2, $age) ;
$prep->execute() ;
Autre façon de faire des requêtes paramétrées :
$req="SELECT id,prenom FROM ma_table WHERE nom= :nomlu AND age< :agelu" ;
$prep = $connex->prepare($req) ;
$age=44 ;
$prep->bindValue( ‘:nomlu’,'Dalton') ;
$prep->bindValue(‘ :agelu’,$age) ;
$prep->execute() ;
Différence entre bindparam et bindvalue :
$sex = 'male';
$s = $dbh->prepare('SELECT name FROM students WHERE sex = :sex');
$s->bindParam(':sex', $sex); //bindParam relie le paramètre :sex à la variable
$sex
$sex = 'female';
$s->execute(); //executed with WHERE sex = 'female'
Alors que
$sex = 'male';
$s = $dbh->prepare('SELECT name FROM students WHERE sex = :sex');
$s->bindValue(':sex', $sex); //bindvalue relie le paramètre :sex à la valeur de la
variable $sex
$sex = 'female';
$s->execute(); //executed with WHERE sex = 'male'