Qu'est-ce que l'injection SQL et comment la prévenir ?
Une analyse pratique des attaques par injection SQL, des exemples concrets et les solutions qui fonctionnent réellement dans le code en production.
L'injection SQL existe depuis la fin des années 1990 et reste l'un des moyens les plus courants de compromettre une application web. Le bug est simple : un attaquant envoie une entrée qui est interprétée comme du code SQL au lieu de données brutes, et la base de données fait quelque chose qu'elle ne devrait jamais faire.
Comment l'attaque fonctionne réellement
Imaginez un formulaire de connexion qui construit une requête comme ceci en PHP :
$query = "SELECT * FROM users WHERE username = '$username' AND password = '$password'";
Si l'application ne nettoie pas l'entrée, un attaquant tape admin' -- dans le champ nom d'utilisateur. La requête devient :
SELECT * FROM users WHERE username = 'admin' --' AND password = ''
Le -- commente le reste de la ligne, donc la vérification du mot de passe n'a jamais lieu. C'est un contournement classique d'authentification. Les attaquants utilisent aussi UNION SELECT pour extraire des données d'autres tables, ou empilent les requêtes avec un point-virgule pour exécuter quelque chose de destructeur comme DROP TABLE users; si le pilote accepte plusieurs instructions.
Il existe aussi l'injection SQL aveugle, où l'application n'affiche pas directement les résultats des requêtes. Les attaquants déduisent les informations par le timing (SLEEP(5) dans MySQL) ou par les réponses booléennes (la page se comporte-t-elle différemment pour une condition vraie ou fausse). Des outils comme sqlmap automatisent ce genre d'extraction une fois qu'un paramètre vulnérable est trouvé.
Pourquoi la concaténation de chaînes est le problème racine
Chaque variante de cette attaque revient à une seule chose : mélanger du code et des données dans la même chaîne. La base de données ne peut pas distinguer une valeur légitime d'une syntaxe injectée car elles arrivent par le même canal. L'échappement des guillemets aide dans certains cas mais c'est fragile — les encodages différents, l'injection de second ordre (les données stockées une fois, puis réutilisées de façon non sécurisée plus tard) et les quirks spécifiques au pilote créent tous des chemins de contournement. L'échappement est un correctif, pas une solution.
Les requêtes paramétrées sont la vraie solution
La solution est de séparer le code SQL des données utilisateur au niveau du pilote, en utilisant des requêtes paramétrées (aussi appelées prepared statements). La base de données reçoit d'abord la structure de la requête, puis lie les valeurs ensuite, donc l'entrée utilisateur ne peut jamais changer le sens de la requête.
En Python avec psycopg2 :
cur.execute("SELECT * FROM users WHERE username = %s AND password = %s", (username, password))
En Node.js avec mysql2 :
connection.execute('SELECT * FROM users WHERE username = ? AND password = ?', [username, password]);
En Java avec JDBC :
PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE username = ? AND password = ?");
stmt.setString(1, username);
stmt.setString(2, password);
Notez le motif : les espaces réservés (%s, ?) tiennent la place des données, et les valeurs réelles sont passées séparément. Pas de concaténation de chaînes, pas d'échappement manuel nécessaire. Cela fonctionne pour la grande majorité des requêtes que vous écrirez dans une application normale.
Qu'en est-il des noms de tables ou de colonnes dynamiques ?
La paramétrisation gère les valeurs mais pas les identifiants — vous ne pouvez pas lier un nom de table en tant que paramètre. Si votre application doit sélectionner une table de manière dynamique (rare, et souvent un mauvais design), établissez une liste blanche des valeurs autorisées comparées à une liste codée en dur au lieu de faire confiance directement à l'entrée utilisateur :
allowed_tables = {'orders', 'invoices', 'customers'}
if table_name not in allowed_tables:
raise ValueError("Invalid table")
Ne construisez jamais les noms d'identifiants via le formatage de chaînes à partir de l'entrée utilisateur, même avec l'échappement.
Défenses en couches au-delà de la requête elle-même
Les requêtes paramétrées sont le contrôle primaire, mais quelques autres éléments importent :
- Privilèges minimaux sur le compte de base de données. L'utilisateur de la base de données de l'application ne devrait pas avoir
DROP,ALTERou accès à des schémas non liés. Si une injection passe quand même, les privilèges limités limitent les dégâts. - Les ORMs aident par défaut. L'ORM de Django, SQLAlchemy et Hibernate paramètrent tous les requêtes automatiquement quand vous utilisez leurs méthodes standard de création de requêtes. Le risque réapparaît quand les développeurs passent à du SQL brut ou utilisent des appels
.extra()/text()avec interpolation de chaînes — auditez donc ces endroits spécifiquement. - La validation d'entrée est une couche secondaire, pas un remplacement. Vérifier qu'un champ email ressemble à un email est une bonne pratique, mais cela n'arrête pas l'injection en soi — les attaquants trouvent des charges créatives qui passent quand même une validation lâche.
- Un WAF peut détecter des motifs d'attaque connus, mais c'est une couche de détection, pas une solution au code sous-jacent.
Tester votre propre code pour ce bug
Faites passer vos requêtes par un outil d'analyse statique (Bandit pour Python, Semgrep avec des rulesets d'injection SQL) dans le cadre de votre CI. Pour tester manuellement, essayez d'injecter un guillemet simple (') dans chaque champ d'entrée et guettez les messages d'erreur SQL qui fuient dans la réponse — c'est souvent le premier signe qu'une requête n'est pas paramétrée.
Si vous voulez approfondir, la piste Web Security de Korra Studio couvre l'injection aux côtés du XSS et du contournement d'authentification, et les segments sur les bases de données montrent les modèles de conception de requêtes qui évitent entièrement cette classe de bug.
Rédigé avec l'aide de l'IA, relu et publié par Michal Pilch (CISSP), Korra Studio.
Ceci est une note de la base de connaissances de Korra Studio — la plateforme associe chaque sujet à un mentorat individuel.
Commencer gratuitementarrow_forward