Eviter les injections SQL Procédures stockées et clause LIMIT : autres tactiques pour éviter les injections SQL

Voici la dernière tactique de protection envisageable : l'utilisation de procédures stockées. L'approche est la même que pour l'utilisation des variables MySQL : on déporte le problème de l'injection dans un cadre où cette dernière ne pourra pas avoir d'effet direct sur la signification de la requête.

DROP PROCEDURE identifie;
DELIMITER //
CREATE PROCEDURE identifie (IN l char(20), IN p char(32), OUT user INT)
     BEGIN
       SELECT COUNT(*) INTO user FROM utilisateurs WHERE login = l AND passe = p;
     END;
     //

DELIMITER ;


L'appel à une telle procédure devient alors :

$requete = " CALL IDENTIFIE(' " .mysqli_real_escape_string($mid, $_POST['login']).
?"','" .mysqli_real_escape_string($mid, $_POST['passe']). "',@id) ";
// exécution de la première requête
$requete = " SELECT @id; "

Sous cette forme, une injection ne peut pas modifier la commande utile, c'est-à-dire l'identification, qui est bien protégée à l'intérieur de l'appel de la procédure et ne peut plus être détournée de son objectif final.

Les injections restent possibles au niveau des valeurs qui sont fournies à la procédure stockée. Il faut donc appliquer les protections d'usage.

Savoir limiter les dégâts

Une approche prudente dans la manipulation des données sur le serveur est de lui indiquer le nombre maximal de lignes à manipuler. Si jamais une erreur se glisse dans la requête SQL, cela aura un impact sur un nombre limité de lignes et non plus sur l'ensemble des données.

MySQL propose une clause originale en SQL : LIMIT. Cette clause est disponible avec les différentes commandes de manipulation de données et permet de réduire le nombre de lignes concernées. Prenons un exemple :

 mysql> SELECT colonne FROM table LIMIT 10;
mysql> DELETE FROM table LIMIT 10;
mysql> UPDATE table SET colnne = ' valeur ' WHERE id = 1000 LIMIT 1;

La sélection lit les lignes de la table table, mais ne retourne que les 10 premières, si elles existent. Grâce à cette approche, le script PHP qui lit des données ne pourra pas être submergé par un tsunami de lignes.

Dans le cas de la mise à jour ou de l'effacement, l'ajout de la clause LIMIT garantit que la modification des données n'ira pas plus loin que le nombre de lignes spécifié. Si vous devez modifier une fiche d'utilisateur dans la table, LIMIT 1 s'assurera qu'une seule ligne sera effectivement réécrite.

LIMIT ne garantit pas que la ligne qui sera effacée ou modifiée sera celle que vous vouliez effectivement manipuler : elle garantit simplement que ce n'est pas toute la table qui sera modifiée.

mysql> DELETE FROM table LIMIT 10;

Si jamais vous deviez faire une erreur, cette requête serait bien moins dommageable à votre application que celle-ci :

mysql> DELETE FROM table;


Enfin, du point de vue MySQL, il est aussi possible de limiter le nombre de lignes lues par une sélection avec select_limit et max_join_size. On peut également éviter les UPDATE qui ne contiennent pas de clause WHERE avec safe-updates. Nous présenterons plus en détail la configuration du client MySQL au chapitre 8.