J'imagine que vous avez déjà entendu parler des nombreuses fuites de données perpétrées par des acteurs malveillants. Parfois, plusieurs vulnérabilités sont utilisées pour obtenir et extraire les données convoitées, mais parfois, une simple injection permet d'accéder à l'ensemble de la base de données.
Aujourd'hui, nous abordons une vulnérabilité relativement connue, peut-être l'une des plus célèbres sur le Web : l'injection SQL. Mais avant de parler de l'injection SQL, nous devons comprendre le langage SQL et les architectures web. C'est parti.
SQL signifie structured query language, et c'est un langage de programmation permettant d'interagir avec des bases de données relationnelles. Il permet de stocker, de manipuler et de récupérer des données de manière structurée.
Les bases de données relationnelles sont composées d'un ensemble de tables, qui sont utilisées pour stocker des données.
Par exemple, pour mon site web de blog, j'ai ma base de données qui comprend une table blog stockant les articles et une table commentaires, stockant - je pense que vous l'avez deviné - les commentaires.
![]() |
|---|
| Exemple de base de données et de table SQL |
Stocker des données, c'est bien, mais les consulter, c'est encore mieux, c'est pourquoi il existe la requête SELECT en SQL pour récupérer des informations. Par exemple, SELECT * FROM users (ici, nous voulons toutes les données de la table utilisateurs).
![]() | ![]() |
|---|---|
| Requête SELECT ALL | Résultat de SELECT ALL |
WHERE dans une requête SELECT, qui vous permet simplement d'ajouter des conditions spécifiques.![]() | ![]() |
|---|---|
| Sélection avec WHERE | Résultat de la sélection avec WHERE |
![]() | ![]() |
|---|---|
| Sélection avec des conditions multiples | Résultat de la sélection avec des conditions multiples |
SUM, AVG ou COUNT, calculent simplement des valeurs à partir des données de la table.UNION, qui sera important plus tard, utilisé pour combiner les résultats de deux requêtes SELECT distinctes en une seule liste de résultats.![]() | ![]() |
|---|---|
| Requête SQL avec UNION | Résultat de la requête SQL UNION |
SELECT, telles que INSERT, qui insère de nouvelles données dans une table, UPDATE, qui met à jour des données existantes, et DELETE, qui supprime des données.Maintenant que vous avez compris, jetons un coup d'œil rapide aux architectures web.
La plupart des sites web fonctionnent avec une base de données sur un serveur tiers appelé BACKEND. Il existe plusieurs types d'architectures, telles que :
Rappelez-vous simplement que dans toutes ces architectures, le site web envoie des requêtes à la base de données pour échanger des informations, par exemple, pour récupérer tous les commentaires sur une page d'actualités.
Maintenant que les bases ont été posées, plongeons dans le vif du sujet : l'injection SQL !
L'injection SQL (SQLi) est une vulnérabilité de sécurité Web qui permet à un attaquant d'interférer avec les requêtes d'une application à sa base de données.
Cela permet généralement à un attaquant d'afficher des données auxquelles il n'aurait normalement pas accès. Cela peut inclure des données appartenant à d'autres utilisateurs ou toutes autres données auxquelles l'application elle-même peut accéder.
Dans de nombreux cas, un attaquant peut modifier ou supprimer ces données, provoquant des changements persistants dans le contenu ou le comportement de l'application.
Vous vous demandez peut-être comment cela fonctionne ? Voici un petit laboratoire pour vous aider à comprendre !
Il est bon de connaître les injections SQL de base, mais il y a un problème : comment récupérer des données d'autres tables ?
Eh bien, vous pouvez le faire avec l'opérateur UNION. L'injection SQL basée sur UNION permet à un attaquant de récupérer des données d'autres tables.
Comment cela fonctionne-t-il ?
Pour qu'une requête UNION fonctionne, deux conditions essentielles doivent être remplies :
![]() | ![]() |
|---|---|
| SQL union query okay | SQL union query pas okay (pck nombre différent de colonnes) |
![]() | ![]() |
|---|---|
| Requête UNION SQL correcte | Requête UNION SQL incorrecte (en raison de types de données différents entre Age et Nom) |
![]() |
|---|
| Trouver le bon nombre de colonnes pour une injection SQL basée sur UNION |
![]() |
|---|
| Trouver le bon type de données de la colonne pour une injection SQL basée sur UNION |
Maintenant que nous avons compris la théorie, passons à la partie pratique !
L'injection SQL aveugle, ou Blind SQLi, est assez courante. Elle est appelée "aveugle" car, dans ce type d'injection SQL, la réponse HTTP ne contient pas les résultats de notre injection SQL ni d'éventuelles erreurs de la base de données.
Dans ce genre de contexte, plusieurs solutions existent pour continuer à exploiter une injection SQL. Je ne vais pas passer en revue toutes les techniques dans ce post car il est déjà assez long, et les couvrir toutes le rendrait interminable. Cependant, nous passerons brièvement en revue quelques méthodes d'injection SQL aveugle.
Il est possible d'effectuer une injection SQL basée sur le délai de réponse. Voici comment cela fonctionne :
L'attaquant utilise des techniques de temporisation pour déclencher des retards significatifs dans la réponse de l'application. Cela peut être réalisé en utilisant des instructions SQL telles que WAITFOR DELAY ou des fonctions de temporisation spécifiques à la base de données.
L'attaquant analyse ensuite le comportement de l'application. Par exemple, si la réponse est retardée pendant une certaine période, cela peut indiquer que la condition injectée dans la requête est vraie.
De plus, en fonction du temps de réponse, il est possible de brute forcer des colonnes et des tables. Par exemple, dans le gif suivant, un attaquant tente de forcer le nom de la base de données. En commençant par a%, il parcourt l'alphabet entier, et lorsque l'attaquant remarque un temps de réponse plus long, il passe à la lettre suivante. Cela permet de deviner le nom de la table, de la colonne ou d'autres éléments de la base de données.
![]() |
|---|
| Bruteforce d'un mot de passe via injection blind SQLi basée sur le temps |
Les injections SQL basées sur des erreurs conditionnelles sont plus faciles à comprendre. Prenons un exemple où une application web utilise des cookies pour déterminer si un utilisateur est connu, en utilisant une requête SQL comme celle-ci :
![]() |
|---|
| Requête SQL sur un cookie |
Même si la réponse du serveur ne renvoie pas les résultats de la requête, certaines données sont envoyées via un message de Bienvenue sur la page si l'utilisateur est reconnu.
Ce comportement est suffisant pour exploiter la vulnérabilité d'injection SQL à l'aveugle et récupérer des informations en déclenchant conditionnellement différentes réponses, en fonction d'une condition injectée.
Par exemple, avec ces charges utiles(payload), si l'injection réussit, la première retournera le message Bienvenue, tandis que la deuxième ne le fera pas :
![]() |
|---|
| Charge utile possible pour une injection SQL conditionnelle |
![]() |
|---|
| Identification d'une injection SQL conditionnelle |
De la même manière que pour les attaques basées sur le temps, vous pouvez effectuer une force brute sur les tables ou les mots de passe en fonction du message qui dépend de la requête SQL :
![]() |
|---|
| Charge utile possible pour une injection SQL conditionnelle (force brute) |
![]() |
|---|
| Bruteforce via injection SQL conditionnelle |
Si vous ne parvenez pas à récupérer les résultats directement depuis le serveur, une approche consiste à envoyer les résultats vers un serveur que vous contrôlez. C'est l'objectif de l'injection SQL aveugle hors bande (out-of-band).
Dans ce scénario, un attaquant exfiltre des données de la table users où le nom d'utilisateur est administrateur vers un nom de domaine contrôlé par l'attaquant.
![]() |
|---|
| Charge utile possible pour une injection SQL OAST (out-of-band) |
![]() |
|---|
| Exemple d'injection SQL hors bande |
Jusqu'à présent, ce que nous avons vu, ce sont des injections SQL de premier ordre. Dans ce cas, l'application prend en compte les entrées de l'utilisateur à partir d'une requête HTTP et, lors du traitement de cette requête, incorpore les entrées dans une requête SQL non sécurisée, et nous obtenons le résultat dans la réponse HTTP.
L'injection SQL de second ordre est un peu différente. Dans ce scénario, l'application prend les entrées de l'utilisateur à partir d'une requête HTTP et les stocke pour une utilisation future. Cela se fait généralement en plaçant les entrées dans une base de données, mais aucune vulnérabilité n'apparaît au moment du stockage des données. Plus tard, lors du traitement d'une autre requête HTTP, l'application récupère les données stockées et les incorpore dans une requête SQL non sécurisée.
| Injection SQL de second ordre (portswigger.net) |
Maintenant que nous avons passé en revue les différents types d'injections SQL, passons aux impacts, même si vous avez probablement déjà une idée en fonction des exemples :
![]() |
|---|
| Exemple de requête préparée |
![]() |
|---|
| Code pour filtrer les entrées |
Portswigger
Wikipedia