Injection de SQL, le danger en résumé
Extraits condensés de la CWE-89: Improper Neutralization of Special Elements used in an SQL Command
Il suffit de préparer ses requêtes pour l'éviter, avec mysqli::prepare ou mysqli_prepare ou PDO::prepare pour PHP. Sinon, quel est le risque avec ce genre de requête non-préparée SELECT * FROM utilisateurs WHERE nom = '$username'
?
En admettant qu'on met imprudemment dans la variable $username
une valeur récupérée de la requête HTTP, non-filtrée par une regexp ou autre :
-
Si un attaquant passe en paramètre de la requête HTTP
bidule' OR 'a'='a
on va envoyer à la base de donnéesSELECT * FROM utilisateurs WHERE nom = 'bidule' OR 'a'='a'
ce qui revient à direSELECT * FROM utilisateurs
donc si on affiche le résultat, on va afficher tout le contenu de la base des utilisateurs 🤦 -
Si un attaquant passe en paramètre de la requête HTTP
bidule'; DELETE FROM utilisateurs; SELECT 'a
on va envoyer à la base de données
SELECT * FROM utilisateurs WHERE nom = 'bidule'; DELETE FROM utilisateurs; SELECT 'a'
ce qui va exécuter 3 requêtes dont une qui efface tous les utilisateurs 😱 -
L'attaquant n'a même pas besoin qu'on affiche le résultat de la requête SQL pour deviner des choses dans la base.
Il peut faire une injection en aveugle dont le temps d'exécution va lui fournir l'information qu'il cherche.
Par exemple, s'il passe en paramètre de la requête HTTP
b'; select if(substring(nom, 1, 1) = 'a', SLEEP(5), null) from utilisateurs where id = '1
on va envoyer à la base de données
SELECT * FROM utilisateurs WHERE nom = 'b'; select if(substring(nom, 1, 1) = 'a', SLEEP(5), null) from utilisateurs where id = '1'
L'attaquant se fiche du résultat de la première requêteSELECT * FROM utilisateurs WHERE nom = 'b'
Ce qui l'intéresse, c'est de savoir si le nom de l'utilisateur n° 1 (a priori un administrateur) commence par un a. Si c'est le cas, la deuxième requête va dormir 5 secondes. Si ce n'est pas le cas, elle va retourner null immédiatement. Il va passer en paramètre successivement toutes les lettres de l'alphabet, et en fonction du temps de réponse des requêtes HTTP, il finira par deviner le nom de l'administrateur.
(un bon article sur ce sujet, concernant WordPress en particulier) - Il existe même des bases de données qui peuvent exécuter des commandes système avec une certaine syntaxe. C'est surprenant mais, à ma connaissance, ça ne concerne pas Mysql/MariaDB.
Bref, il faut toujours préparer ses requêtes de cette façon : SELECT * FROM utilisateurs WHERE nom = ?