L'injection SQL permet aux attaquants d'interférer avec les requêtes de base de données pour extraire, modifier ou supprimer des données.
TL;DR
$queryRawUnsafe, raw(), text() avec f-strings contournent toute protectionL'injection SQL (CWE-89, OWASP A03:2021) survient lorsqu'une entrée fournie par l'utilisateur est concaténée directement dans une requête SQL au lieu d'être liée comme paramètre. Le moteur de base de données interprète les jetons contrôlés par l'attaquant comme de la syntaxe SQL, les exécutant avec les mêmes privilèges que le compte de base de données de l'application. Le DBIR Verizon 2024 a lié 26 % de toutes les violations de données à des attaques d'applications web incluant l'injection SQL. OWASP A03:2021 (Injection) recense plus de 14 000 CVE mappés à CWE-89 et note que 100 % des applications testées présentaient au moins un résultat d'injection.
La vulnérabilité n'est pas limitée aux codebases PHP historiques. CVE-2025-25257 (Fortinet FortiWeb, CVSS 9.6) a été activement exploité dans la nature en juillet 2025, permettant à des attaquants non authentifiés d'enchaîner l'injection SQL avec INTO OUTFILE pour un RCE root. CVE-2024-43468 (Microsoft SCCM, CVSS 9.8) a rejoint le catalogue des vulnérabilités exploitées connues (KEV) de la CISA en février 2026. CVE-2024-42005 (Django, CVSS 9.8) a démontré que même les appels ORM de haut niveau — pas du SQL brut — peuvent être injectables lorsque des alias de colonnes JSON sont construits à partir de données utilisateur.
La surface d'attaque s'est étendue au-delà des paramètres GET/POST classiques. Les vecteurs d'injection incluent désormais les arguments GraphQL, les en-têtes HTTP journalisés en base de données, les champs de corps JSON, les fonctions échappatoire ORM, et les chaînes SQL générées par IA/LLM. Les scanners DAST classiques et les WAF manquent fréquemment ces vecteurs plus récents.
Lorsqu'une application construit une requête par concaténation de chaînes, l'attaquant contrôle une partie de l'arbre de parse SQL. L'exemple canonique : un formulaire de connexion qui construit SELECT * FROM users WHERE username = ' + input + '. Injecter admin'-- ferme le littéral de chaîne et commente la vérification du mot de passe, contournant l'authentification sans connaître aucun identifiant.
Le cycle de vie de l'injection comporte quatre étapes : (1) identifier un paramètre injectable en observant les changements de réponse aux caractères guillemets ; (2) empreinte du moteur de base de données via les messages d'erreur ou le timing conditionnel ; (3) énumérer le schéma via information_schema, sysobjects, ou les tables de catalogue natives ; (4) extraire les données cibles ou escalader vers l'exécution de code.
Les six variantes principales diffèrent dans la façon dont les données atteignent l'attaquant et quel canal en bande ou hors-bande est disponible.
| Variante | Canal de données | Prérequis | CVSS typique | Page |
|---|---|---|---|---|
| Basée sur UNION | Réponse HTTP (en bande) | Résultat reflété, nombre de colonnes connu | 9.8 | ↗ |
| Basée sur les erreurs | Réponse HTTP (en bande) | Erreurs DB verbeuses reflétées | 9.8 | ↗ |
| Booléen à l'aveugle | Différence de réponse (inférentielle) | Changements de page/statut/contenu sur vrai vs faux | 8.8 | ↗ |
| À l'aveugle basée sur le temps | Timing (inférentielle) | Délai de réponse mesurable | 8.8 | ↗ |
| Hors-bande (OOB) | Callback DNS/HTTP (OOB) | Accès réseau sortant du serveur DB | 9.8 | ↗ |
| Second ordre (Stockée) | En bande/OOB différé | Valeur stockée exécutée dans une deuxième requête | 9.1 | → voir ci-dessous |
Basée sur UNION est la plus rapide pour l'extraction de données quand les résultats de requête sont reflétés. Basée sur les erreurs s'applique quand les messages d'erreur verbeux sont activés. Booléen à l'aveugle et Basée sur le temps couvrent les cas où aucune sortie directe n'est visible. OOB est utilisée quand tous les canaux en bande sont supprimés ou asynchrones. L'injection de second ordre est systématiquement manquée par les outils automatisés.
Comprendre la syntaxe spécifique à chaque moteur est essentiel pour tester et confirmer l'injection sur différents backends.
| Fonctionnalité | MySQL | PostgreSQL | MSSQL | Oracle | SQLite |
|---|---|---|---|---|---|
| Fonction sleep | SLEEP(N) | pg_sleep(N) | WAITFOR DELAY '0:0:N' | dbms_pipe.receive_message('a',N) | randomblob(N*1000) |
| Requête de version | @@version | version() | @@version | v$version | sqlite_version() |
| Concaténation | CONCAT() ou espace | || | + | || | || |
| Styles de commentaire | # ou -- | -- | -- | -- | -- |
| Requêtes empilées | Partiel (PDO) | Oui | Oui | Non | Oui |
| FROM requis | Non | Non | Non | Oui (DUAL) | Non |
| Fonction de fuite d'erreur | EXTRACTVALUE() | CAST(x AS int) | CONVERT(int,x) | CTXSYS.DRITHSX.SN() | LIKE lourd |
| Exfil OOB | LOAD_FILE() UNC | COPY TO PROGRAM | xp_dirtree | UTL_HTTP | Aucun natif |
Les développeurs supposent souvent que l'utilisation d'un ORM élimine le risque d'injection SQL. Cette hypothèse est correcte pour les méthodes de requête ORM standard. Elle s'effondre immédiatement au niveau des fonctions échappatoire qui contournent la couche de paramétrage.
// Prisma — $queryRawUnsafe EST vulnérable
const users = await prisma.$queryRawUnsafe(
`SELECT * FROM "User" WHERE email = ${untrustedInput}` // VULNÉRABLE
)
// Prisma — $queryRaw avec template taggé EST sûr
const users = await prisma.$queryRaw`SELECT * FROM "User" WHERE email = ${untrustedInput}`# SQLAlchemy — text() avec f-string EST vulnérable
db.execute(text(f"SELECT * FROM users WHERE name = '{user_input}'")) # VULNÉRABLE
# SQLAlchemy — text() avec paramètres liés EST sûr
db.execute(text("SELECT * FROM users WHERE name = :name"), {"name": user_input})# Django — raw() avec f-string EST vulnérable
User.objects.raw(f"SELECT * FROM users WHERE name = '{name}'") # VULNÉRABLE
# Django — raw() avec paramètres EST sûr
User.objects.raw("SELECT * FROM users WHERE name = %s", [name])CVE-2025-64459 (Django, CVSS 9.1) a démontré que même les appels ORM standard peuvent être injectables lorsque des dictionnaires contrôlés par l'utilisateur sont développés dans filter(**user_input) sans valider les paramètres internes des objets Q().
$queryRawUnsafe (Prisma), text() avec f-strings (SQLAlchemy), raw() avec chaînes de format (Django), et session.createQuery() avec concaténation de chaînes (Hibernate) contournent entièrement la paramétrage ORM. Auditez chaque appel de requête brute dans votre codebase — pas seulement les plus évidents.
En 2022, Claroty Team82 a publié des recherches montrant que cinq grands fournisseurs WAF — Palo Alto, AWS WAF, Cloudflare, F5 et Imperva — ne vérifiaient pas la syntaxe JSON pour les patterns SQL. Les bases de données SQL modernes supportent les opérateurs JSON nativement, permettant à des payloads comme l'opérateur de contenance PostgreSQL (<@) ou JSON_EXTRACT() MySQL de passer l'inspection WAF sans être détectés.
-- PostgreSQL : opérateur de contenance — le WAF voit du JSON, le DB exécute un contexte SQL
'{"b":2}'::jsonb <@ '{"a":1, "b":2}'::jsonb
-- MySQL : fonction JSON comme enveloppe d'injection
JSON_EXTRACT('{"id": 14}', '$.name') = 'admin' OR 1=1--Les cinq fournisseurs ont corrigé après une divulgation coordonnée en 2022–2023. Les déploiements anciens et les jeux de règles WAF personnalisés peuvent rester vulnérables. Ce contournement est également efficace contre la commutation de type de contenu : de nombreux WAF appliquent les règles d'injection SQL uniquement à application/x-www-form-urlencoded et ignorent entièrement les corps application/json.
Heartland Payment Systems (2008) — Une injection SQL contre le réseau interne du processeur de paiement a compromis environ 130 millions de dossiers de cartes de crédit et de débit. À l'époque, il s'agissait de la plus grande violation de données jamais enregistrée. Les attaquants ont utilisé l'injection SQL initiale pour prendre pied, puis ont installé des renifleurs de paquets sur le réseau de traitement des transactions.
Equifax (2017) — La violation affectant 143 millions de consommateurs américains incluait l'injection SQL dans la chaîne d'attaque exploitant Apache Struts. Equifax avait été averti de la vulnérabilité des mois avant l'exploitation. La violation a abouti à 700 millions de dollars de règlements.
MOVEit (2023) — La plateforme de partage de fichiers MOVEit Transfer de Progress Software contenait une vulnérabilité d'injection SQL zero-day (CVE-2023-34362, CVSS 9.8). Le groupe de ransomware Cl0p l'a exploitée contre 80 % des victimes d'entreprises américaines avant qu'un correctif soit disponible. Les données des employés d'Amazon figuraient parmi les enregistrements volés et vendus.
CVE-2024-43468 (Microsoft SCCM, CVSS 9.8) — Injection SQL non authentifiée dans le service MP_Location de Microsoft Configuration Manager. L'exploitation active xp_cmdshell, accordant une exécution complète de commandes OS sur le serveur SCCM. Synacktiv a publié un PoC public en novembre 2024 ; la CISA a ajouté ceci au KEV en février 2026.
L'injection SQL de second ordre (stockée) contourne la validation des entrées au point d'entrée car le payload paraît sûr lors du stockage. Il s'exécute quand une fonction séparée récupère la valeur stockée et la réutilise dans une nouvelle requête sans la re-paramétrer. Les scanners DAST standard ne peuvent pas détecter ceci sans configuration explicite de second URL.
L'injection SQL de second ordre stocke le payload de manière sûre à un endpoint, puis le récupère et l'exécute à un endpoint différent. L'attaque exploite la fausse hypothèse qu'une fois les données stockées, elles sont « fiables » pour une utilisation future.
1. Stocker : POST /api/report/ {"dateRange": "2024-08-15'"} → 200 OK (échappé, stocké)
2. Vérifier : GET /api/report/ExportToExcel → 500 Erreur (SQL exécuté)
3. Confirmer : POST avec {"dateRange": "2024-10-06';--"} → 200 OK à la vérification
4. Timing : WAITFOR DELAY '0:0:20' via stockage → délai 20s à la vérification
5. OOB : xp_dirtree via stockage → callback DNS avec DB_NAME()Les paires endpoint store → verify courantes incluent /register → /admin/users, /profile/update → /dashboard, et /api/report/ → /api/report/ExportToExcel. SQLMap gère les tests de second ordre avec --second-url.
') ajoutée à la valeur du paramètre et observer si la réponse change en code de statut, longueur de contenu ou corps de contenu.AND 1=1-- (condition vraie) et AND 1=2-- (condition fausse). Une réponse différente pour chacun confirme l'injection différentielle booléenne.' OR SLEEP(3)-- et mesurer le temps de réponse. Un délai de 3 secondes confirme l'injection basée sur le temps.X-Forwarded-For, User-Agent, Referer et Cookie journalisées en base de données sont fréquemment injectables.{"filter": "value"} en {"filter": "value' OR 1=1--"} et observer les différences.# Scan de référence sqlmap avec authentification
sqlmap -u "https://target.com/search?q=1" --batch --dbs
# Injection dans le corps JSON
sqlmap -u "https://target.com/api/users" \
--data='{"filter":"*"}' --content-type="application/json" \
--technique=BEUST --batch
# Injection dans les en-têtes
sqlmap -u "https://target.com/" \
--headers="X-Forwarded-For: *" --level=3 --batch
# Second ordre
sqlmap -u "https://target.com/store" \
--data="field=*" --second-url="https://target.com/trigger" --batchBreachVex détecte l'injection SQL via plusieurs techniques complémentaires : mesure de référence, sondage avec guillemet ajouté, analyse différentielle booléenne vrai/faux, et confirmation de timing proportionnelle pour les variantes basées sur le temps.
Les requêtes paramétrées lient l'entrée utilisateur comme donnée, jamais comme syntaxe SQL. Cette défense fonctionne au niveau du protocole — elle ne peut être contournée par aucun encodage ou technique d'injection.
# Python psycopg2 — sûr
cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
# Python SQLAlchemy — sûr
db.execute(text("SELECT * FROM users WHERE name = :name"), {"name": name})// Node.js bibliothèque pg — sûr
const result = await client.query(
'SELECT * FROM users WHERE username = $1', [username]
);// Java PreparedStatement — sûr
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE username = ?"
);
stmt.setString(1, username);Les noms de tables, les noms de colonnes et la direction ORDER BY ne peuvent pas être paramétrés. Utilisez des listes d'autorisation strictes.
ALLOWED_COLUMNS = {'id', 'name', 'email', 'created_at'}
ALLOWED_DIRECTIONS = {'ASC', 'DESC'}
def safe_sort(column, direction):
if column not in ALLOWED_COLUMNS or direction not in ALLOWED_DIRECTIONS:
raise ValueError("Paramètre de tri invalide")
return f"ORDER BY {column} {direction}"Le compte de base de données de l'application ne doit détenir que les permissions dont il a besoin : SELECT sur les tables en lecture seule, INSERT/UPDATE sur les tables d'écriture spécifiques. Ne jamais accorder FILE (MySQL), les droits d'exécution xp_cmdshell (MSSQL), ou l'accès COPY TO PROGRAM (superutilisateur PostgreSQL). Un attaquant exploitant une injection SQL ne peut faire que ce que le compte de base de données est autorisé à faire.
L'injection SQL est-elle encore une menace en 2026 ? CWE-89 se classe #3 sur le Top 25 CWE MITRE/CISA (2024). La CISA a catalogué 4 CVE SQLi exploités en 2024 uniquement. CVE-2025-25257 (FortiWeb) a été activement exploité dans la nature en juillet 2025.
L'utilisation d'un ORM protège-t-elle contre l'injection SQL ? Les méthodes ORM standard génèrent des requêtes paramétrées et sont sûres. Les fonctions échappatoire ($queryRawUnsafe, raw(), text() avec f-strings) contournent toutes les protections. CVE-2025-64459 (Django, CVSS 9.1) a démontré que même les appels ORM non-bruts peuvent être injectables via la manipulation de paramètres internes.
L'injection SQL peut-elle contourner un WAF ? Oui. La recherche de Claroty Team82 de 2022 a contourné simultanément cinq grands WAF en utilisant des opérateurs JSON. Les techniques supplémentaires incluent l'injection de commentaires, l'encodage d'URL, la fragmentation de mots-clés et la pollution de paramètres HTTP. Les WAF sont une défense en profondeur, pas une prévention primaire.
Quelle est la défense la plus efficace ? Les requêtes paramétrées (requêtes préparées) au niveau de la couche applicative. L'alerte Secure by Design de la CISA de mars 2024 appelle explicitement les éditeurs à éliminer l'injection SQL comme classe de vulnérabilité en adoptant universellement les requêtes paramétrées.
L'injection SQL en bande (basée sur UNION, basée sur les erreurs) retourne les données directement dans la réponse HTTP. L'injection SQL à l'aveugle (booléenne, basée sur le temps, OOB) extrait les données indirectement en observant les différences de réponse, les délais ou les callbacks hors-bande.
Les méthodes de requête ORM standard sont sûres car elles génèrent des requêtes paramétrées. Cependant, les échappatoires ORM — Django raw(), SQLAlchemy text() avec f-strings, Prisma $queryRawUnsafe — contournent toutes les protections ORM et sont directement injectables.
Le DBIR Verizon 2024 a attribué 26 % de toutes les violations de données en partie aux attaques d'applications web incluant l'injection SQL. Le rapport 2024 d'Aikido sur l'état des injections SQL a trouvé des SQLi dans 6,7 % des vulnérabilités open-source analysées.
Oui. Sur MSSQL, un attaquant qui réussit une injection SQL peut activer xp_cmdshell via des requêtes empilées et exécuter des commandes OS. Sur PostgreSQL, COPY TO PROGRAM accomplit la même chose avec les privilèges superutilisateur. Sur MySQL, INTO OUTFILE peut écrire un webshell PHP si la racine web est accessible en écriture.
Oui. CWE-89 est #3 sur le Top 25 CWE MITRE/CISA (2024). La CISA a ajouté 4 CVE d'injection SQL à son catalogue de vulnérabilités exploitées connues en 2024. CVE-2025-25257 (FortiWeb, CVSS 9.6) a été activement exploité dans la nature en 2025.
L'injection SQL de second ordre (stockée) stocke un payload à un endpoint et l'exécute quand un autre endpoint récupère la valeur stockée sans la re-paramétrer. Les scanners DAST ne voient aucun changement de réponse immédiat au point d'injection, donc ils ne peuvent pas la détecter sans configuration de second URL.
Claroty Team82 (2022) a démontré que cinq grands fournisseurs WAF — Palo Alto, AWS WAF, Cloudflare, F5, Imperva — n'inspectaient pas la syntaxe JSON pour les patterns SQL. L'injection via les opérateurs JSON PostgreSQL ou les fonctions JSON MySQL permettait aux payloads SQLi de contourner entièrement les règles WAF.
Ajouter une apostrophe à la valeur du paramètre et observer si la réponse change (erreur, contenu différent, changement de code de statut). Puis envoyer AND 1=1-- et AND 1=2-- et comparer les réponses. Une différence entre les deux confirme l'injection différentielle booléenne.