Injections SQL (SQLi) : principes,
impacts, exploitations et bonnes
pratiques de sécurité
Membres du groupe :
- BIKELE Anne-Sophie Glenn
- ETOUNDI NKODO Lauren
- FOKOU SOH Cédric
- MOUGANG NGUETI Yan Kemuel
Sous la supervision de : M. MBALLA Jean Claude
Année académique : 2025 - 2026
INTRODUCTION
SQL(Structure Query Language) Injection, principalement appelée , est une
attaque sur un serveur de base de données d'applications web qui provoque
l'exécution de requêtes malveillantes. Lorsqu'une application web communique
avec une base de données en utilisant l'entrée d'un utilisateur qui n'a pas été
correctement validée, il est possible qu'un attaquant puisse voler, supprimer ou
modifier des données privées et client et s'attaquer aux méthodes d'authentification
de l'application Web vers des zones privées ou clientes. C'est pourquoi est l'une des
vulnérabilités les plus anciennes d'applications web, et elle peut aussi être la plus
dommageable.
Dans ce travail, vous apprendrez quelles sont les bases de données, ce qui est avec
un peu de base commandes, comment détecter vulnérabilités, comment exploiter
les vulnérabilités et, en tant que développeur, comment vous pouvez vous protéger
contre Injection.
I -En quoi consiste une injection SQL (SQLi) ?
1- Définitions de concepts de base
Une base de données est un moyen de stocker électroniquement des
collections de données de manière organisée. Une base de données est contrôlée
par un SGBD, qui est l’acronyme de Database Management System. Les SGBD
se divisent en deux catégories : relationnelles et non relationnelles ; l’accent de
cette salle sera mis sur les bases de données relationnelles ; Parmi les plus
courants que vous rencontrerez, on trouve MySQL, Microsoft SQL Server,
Access, PostgreSQL et SQLite. Nous expliquerons la différence entre les bases
de données relationnelles et non relationnelles à la fin de cette tâche, mais il est
d’abord important d’apprendre quelques termes.
Un tableau est composé de colonnes et de rangées ; Une façon utile
d’imaginer une table est comme une grille où les colonnes vont de gauche à
droite du haut contenant le nom de la cellule et les lignes de haut en bas,
chacune contenant les données réelles.
2- Bases de données relationnelles vs non relationnelles :
Une base de données relationnelle stocke les informations dans des tables,
et souvent, les tables partagent des informations entre elles ; elles utilisent des
colonnes pour spécifier et définir les données stockées et des lignes pour stocker
réellement les données. Les tables contiennent souvent une colonne ayant un
identifiant unique (clé primaire), qui sera ensuite utilisée dans d’autres tables
pour la référencer et créer une relation entre les tables, d’où le nom de base de
données relationnelle.
Les bases de données non relationnelles, parfois appelées NoSQL, sont en
revanche tout type de base de données qui n’utilise pas de tables, colonnes et
lignes pour stocker les données. Une disposition spécifique de base de données
n’a pas besoin d’être construite pour que chaque ligne de données puisse
contenir des informations différentes, offrant ainsi plus de flexibilité par rapport
à une base de données relationnelle. Parmi les bases de données populaires de ce
type figurent MongoDB, Cassandra et ElasticSearch.
3- Le langage SQL
SQL (Structured Query Language) est un langage riche en fonctionnalités
utilisé pour interroger des bases de données. Ces requêtes SQL sont mieux
appelées instructions.
La commande la plus simple que nous aborderons dans cette tâche est
utilisée pour récupérer (sélectionner), mettre à jour, insérer et supprimer des
données. Bien que quelque peu similaires, certains serveurs de bases de données
ont leur propre syntaxe et de légères modifications dans le fonctionnement des
choses. Tous ces exemples sont basés sur une base de données MySQL. Après
avoir appris les leçons, vous pourrez facilement rechercher en ligne des syntaxes
alternatives pour les différents serveurs. Il est important de noter que la syntaxe
SQL n’est pas sensible à la casse.
3.1-SELECT
Le premier type de requête que nous apprendrons est la requête SELECT
utilisée pour récupérer les données de la base de données. Le premier mot
SELECT indique à la base de données que nous voulons récupérer certaines
données ; Le * indique à la base de données que nous voulons recevoir toutes les
colonnes de la table. Par exemple, le tableau peut contenir trois colonnes (id,
nom d’utilisateur et mot de passe). « from users » indique à la base de données
que nous voulons récupérer les données à partir de la table nommée users. Enfin,
le point-virgule à la fin indique à la base de données que c’est la fin de la
requête.
select username,password from users;
3.2- UNION
L’instruction UNION combine les résultats de deux ou plusieurs
instructions SELECT pour récupérer des données à partir d’une seule ou de
plusieurs tables ; les règles de cette requête sont que l’instruction UNION doit
récupérer le même nombre de colonnes dans chaque instruction SELECT, les
colonnes doivent être d’un type de données similaire, et l’ordre des colonnes
doit être le même.
3.3INSERTION
L’instruction INSERT indique à la base de données que nous souhaitons
insérer une nouvelle ligne de données dans la table. « dans les utilisateurs
» indique à la base de données dans quelle table nous souhaitons insérer les
données, « (nom d’utilisateur, mot de passe) » fournit les colonnes pour lesquelles
nous fournissons les données, puis « valeurs ('bob','password') » ; » fournit les
données pour les colonnes précédemment spécifiées.
insert into users (username,password) values ('bob','password123');
3.3- MISE À JOUR
L’instruction UPDATE indique à la base de données que nous souhaitons
mettre à jour une ou plusieurs lignes de données dans une table. Vous spécifiez
la table que vous souhaitez mettre à jour en utilisant « update %tablename %
SET », puis sélectionnez le ou les champs que vous souhaitez mettre à jour
comme une liste séparée par des virgules, comme «
username='root',password='pass123' », puis enfin, à l’instar de l’instruction
SELECT, vous pouvez spécifier exactement quelles lignes mettre à jour en
utilisant la clause where comme « where username='admin ;"
update users SET username='root',password='pass123' where
username='admin';
4- L’injection SQL
Il existe de nombreux types de vulnérabilités par injection, comme les
failles XSS, l’injection d’entête HTTP, l’injection de code ainsi que l’injection
de commande. Cependant, la plus connue, l’une des plus redoutables et la
favorite des attaquants est certainement l’injection SQL. Une injection SQL se
produit lorsqu’un utilisateur malveillant communique une entrée qui modifie la
requête SQL envoyée par l’application web à la base de données. Cela lui
permet alors d’exécuter d’autres requêtes SQL non souhaitées directement sur la
base de données.
Pour ce faire, l’attaquant doit injecter du code en dehors des limites de
l’entrée utilisateur attendue, afin qu’il ne soit pas exécuté comme une entrée
standard. Dans le cas le plus simple, il suffit d’injecter un guillemet simple ou
double pour échapper aux limites de la saisie utilisateur et ainsi insérer des
données directement dans la requête SQL. En effet, si possibilités d’injection il y
a, l’attaquant va chercher un moyen d’exécuter une requête SQL différente.
Dans la plupart des cas, il va utiliser du code SQL pour créer une requête qui
exécute à la fois la requête SQL prévue et la nouvelle requête SQL.
II- Cas d’utilisation et impacts d’une injection
SQL
1- Quelques cas d’utilisation
Une injection SQL peut avoir un impact énorme, surtout si les privilèges
sur le serveur et sur la base de données sont trop permissifs. Tout d’abord, un
attaquant peut récupérer des informations sensibles, comme les identifiants et
mots de passe des utilisateurs ou les informations relatives aux cartes bancaires.
En effet, les injections SQL sont à l’origine de nombreuses compromissions de
mots de passe et de données de sites et d’applications web.
Un autre cas d’utilisation de l’injection SQL consiste à détourner la
logique prévue de l’application web. L’exemple le plus courant est le
contournement d’une page d’authentification. Les attaquants peuvent également
être en mesure de lire et d’écrire des fichiers directement sur le serveur, ce
qui peut conduire à placer des backdoors (portes dérobées) sur le serveur, puis à
prendre le contrôle de l’application.
1.1 Comment détourner la logique d’une application via une
attaque par injection SQL ?
Avant de commencer à exécuter des requêtes SQL entières, nous allons
d’abord étudier comment détourner la logique de la requête originale.
a- Recherche d’un paramètre vulnérable aux SQLi
Avant de parvenir à nos fins, à savoir détourner la logique de l’application
web et contourner l’authentification, nous devons dans un premier temps tester
le formulaire de connexion, pour savoir s’il est vulnérable à l’injection SQL.
Pour ce faire, nous pouvons ajouter l’une des payload ci-dessous après notre
nom d’utilisateur et voir si cela provoque des erreurs ou modifie le
comportement de la page :
Payload URL Encoded
‘ %27
« %22
# %23
; %3B
) %29
Lors de l’ajout d’un simple guillemet, une erreur SQL est affichée. La requête
SQL envoyée à la base de données est la suivante :
Le guillemet que nous avons saisi a donné lieu à un nombre impair de
guillemets, ce qui a provoqué une erreur de syntaxe. Une option serait de
commenter et d’écrire le reste de la requête dans le cadre de notre injection pour
forger une requête fonctionnelle. Une autre option consiste à utiliser un nombre
pair de guillemets dans notre requête injectée, de sorte que la requête finale
fonctionne toujours.
b- Contournement d’authentification via une
attaque par injection SQL
Sur la page d’authentification, nous pouvons nous connecter avec les
informations d’identification de l’administrateur : La page affiche la requête
SQL en cours d’exécution afin de mieux comprendre comment détourner la
logique de la requête.
La page prend en compte les informations d’identification, puis utilise
l’opérateur AND pour sélectionner les enregistrements correspondants au nom
d’utilisateur et au mot de passe renseignés. Si la base de données MySQL
renvoie les enregistrements correspondants, les informations d’identification
sont valides, et le code PHP évalue la condition de tentative de connexion
comme vraie. Si la condition est « True », l’enregistrement de l’administrateur
est renvoyé, et notre connexion est validée.
Au contraire, lorsque de mauvaises informations de connexion sont renseignées,
la connexion échoue et la base de données renvoie « False ».
c- Attaque par injection SQL avec l’opérateur OR
Pour contourner l’authentification, il faudrait que la requête renvoie « True »,
quels que soient le nom d’utilisateur et le mot de passe saisis. Pour ce faire, nous
pouvons abuser de l’opérateur OR dans notre injection SQL.
La documentation MySQL indique que l’opérateur AND est évalué avant
l’opérateur OR. Cela signifie que s’il y a au moins une condition « True » dans
la requête avec un opérateur OR, la requête sera évaluée comme « True »
puisque l’opérateur OR renvoie « True » si l’un de ses opérandes est vrai.
Un exemple de condition qui renvoie toujours TRUE est 1=1. Toutefois, pour
que la requête SQL continue de fonctionner et que le nombre de guillemets soit
pair, au lieu d’utiliser (‘1’=’1’), nous supprimerons le dernier guillemet et
utiliserons (‘1’=’1), de sorte que le guillemet unique restant de la requête
originale sera à sa place.
L’opérateur AND sera évalué en premier, et il renverra « False ». Ensuite,
l’opérateur OR sera évalué, et si l’une des déclarations est vraie, il renverra
« True ». Puisque 1=1 renvoie toujours « True », cette requête renverra vrai et
nous donnera l’accès.
Note : La payload que nous avons utilisé ci-dessus est l’une des nombreuses
payloads de contournement d’authentification que nous pouvons utiliser pour
contourner la logique d’authentification.
Si le nom d’utilisateur n’est pas valide, la connexion va échouer parce qu’il
n’existe pas dans la table et a donné lieu à une fausse requête globale.
d- Contournement de l’authentification avec des
commentaires
Comme tout autre langage, SQL permet également l’utilisation de
commentaires. Les commentaires sont utilisés pour documenter les requêtes ou
ignorer une certaine partie de la requête. Nous pouvons utiliser deux types de
commentaires avec MySQL : — et #.
Comme nous pouvons le voir, le reste de la requête est maintenant ignoré et le
mot de passe n’est plus vérifié. De cette façon, nous pouvons nous assurer que la
requête ne présente aucun problème de syntaxe.
1.2 Focus sur les attaques par injection SQL avec UNION
(Union Based SQLi)
Un autre type d’injection SQL consiste à injecter des requêtes SQL entières
exécutées en même temps que la requête originale.
La clause UNION est utilisée pour combiner les résultats de plusieurs
instructions SELECT. Cela signifie que grâce à une injection UNION, nous
serons en mesure de sélectionner et d’extraire des données de l’ensemble de la
base de données.
UNION a combiné la sortie des deux instructions SELECT en une seule, ainsi
les entrées des tables ont été combinées en une seule sortie.
Une instruction UNION ne peut fonctionner que sur des instructions SELECT
comportant un nombre égal de colonnes. L’UNION de deux requêtes qui ont des
résultats avec un nombre de colonnes différentes renverra une erreur.
S’il y a plus de colonnes dans la table de la requête originale, il faut ajouter
d’autres chiffres afin de créer les colonnes restantes requises.
Comme nous pouvons le voir, le résultat souhaité de la requête se trouve dans la
première colonne de la deuxième ligne, tandis que les chiffres remplissent les
autres colonnes.
a- Identification du nombre de colonnes
Afin d’exploiter les requêtes basées sur la clause UNION, il faut trouver le
nombre de colonnes sélectionnées par le serveur. Il existe deux méthodes pour
détecter ce nombre :
En utilisant ORDER BY
En utilisant UNION
b- Utilisation de ORDER BY
La première façon de détecter le nombre de colonnes est la clause ORDER
BY. La requête injectée va trier les résultats par le nombre de colonne que nous
avons spécifiée jusqu’à ce que nous obtenions une erreur indiquant que la
colonne spécifiée n’existe pas. La dernière colonne par laquelle nous avons
réussi à trier nous donne le nombre total de colonnes.
c- Utilisation de UNION
L’autre méthode consiste à utiliser la clause UNION avec un nombre
différent de colonnes jusqu’à ce que nous obtenions les résultats avec succès.
Contrairement à la méthode précédente, celle-ci donne toujours une erreur
jusqu’à ce que nous obtenions le bon nombre de colonnes.
d-Localisation de l’injection
Alors qu’une requête peut renvoyer plusieurs colonnes, l’application web
peut n’en afficher que certaines. Ainsi, si nous injectons notre requête dans une
colonne qui n’est pas affichée sur la page, nous n’obtiendrons pas son résultat.
C’est pourquoi nous devons déterminer quelles colonnes sont présentes sur la
page, afin de déterminer où placer notre injection.
IV- Énumération de la base de données suite à
un SQLi
Avant d’énumérer la base de données, nous devons identifier le type de Système
de Gestion de Base de Données (SGBD) afin de savoir quelles requêtes utiliser.
Si le serveur web que nous voyons dans les réponses HTTP est Apache ou
Nginx, il est probable que le serveur web soit sous Linux, et donc que le SGBD
soit MySQL. Il en va de même pour le SGBD Microsoft si le serveur web est
IIS, il s’agit donc probablement de MSSQL. Il existe donc différentes requêtes
que nous pouvons tester pour déterminer le type de base de données.
Maintenant, pour extraire des données des tables à l’aide de UNION SELECT,
nous devons former correctement nos requêtes SELECT. Pour ce faire, nous
devons disposer de :
La liste des bases de données
La liste des tables de chaque base de données
La liste des colonnes de chaque table
Avec les informations ci-dessus, nous pourrons formuler notre instruction
SELECT pour extraire toutes les données.
La base de données INFORMATION_SCHEMA contient des métadonnées sur
les bases de données et les tables présentes sur le serveur. Cette base de données
joue un rôle crucial dans l’exploitation des vulnérabilités par injection SQL.
1- Schema
Pour trouver quelles bases de données sont disponibles sur le SGBD, nous
pouvons utiliser INFORMATION_SCHEMA.SCHEMATA, qui contient des
informations sur toutes les bases de données du serveur.
2- Tables
Pour trouver toutes les tables d’une base de données, nous pouvons utiliser
INFORMATION_SCHEMA.TABLES. Cette opération peut être effectuée de la
même manière que celle qui a permis de trouver les noms des bases de données.
3- Colonnes
Pour trouver les noms des colonnes de la table, nous pouvons utiliser la table
COLUMNS de la base de données INFORMATION_SCHEMA. Elle contient
des informations sur toutes les colonnes présentes dans toutes les bases de
données.
4-Données
Maintenant que toutes les informations sont réunies, nous pouvons former notre
requête UNION pour extraire les données des de la base de données.
V- Lecture de fichiers suite à une SQLi
Une injection SQL peut également être utilisée pour effectuer de nombreuses
autres opérations, telles que la lecture et l’écriture de fichiers sur le serveur et
même l’exécution de code à distance sur le serveur.
La lecture de données est beaucoup plus courante que l’écriture de données, qui
est strictement réservée aux utilisateurs privilégiés dans les SGBD modernes, car
elle peut conduire à l’exploitation du système. Dans MySQL, l’utilisateur de la
base de données doit disposer du privilège FILE pour charger le contenu d’un
fichier dans une table.
Plusieurs requêtes SQL permettent de déterminer quel utilisateur exécute les
requêtes.
On peut désormais lister les privilèges des utilisateurs. Nous constatons que le
privilège FILE est listé pour notre utilisateur, ce qui nous permet de lire des
fichiers et même potentiellement d’en écrire.
Grâce à ce privilège FILE, un utilisateur est capable de lire les fichiers du
serveur.
VI- Écriture dans des fichiers suite à une SQLi
L’écriture de fichiers sur le serveur peut être utilisé pour écrire un webshell sur
le serveur distant, ce qui permettra d’exécuter du code et de prendre le contrôle
du serveur.
De la même manière que pour la lecture de fichiers, si l’utilisateur possède les
privilèges suivants, il sera capable d’écrire sur le serveur :
Privilège FILE activé
Variable globale MySQL secure_file_priv n’étant pas activée.
Un accès en écriture à l’emplacement où il veut écrire sur le serveur.
L’instruction SELECT INTO OUTFILE peut être utilisée pour écrire des
données dans des fichiers à partir de requêtes de sélection. Elle est généralement
utilisée pour exporter des données depuis des tables.
Un attaquant peut ainsi uploader un webshell et ainsi accéder au serveur.
VII- Comment contrer les attaques par
injection SQL ?
Dans cette partie, nous allons apprendre à éviter ces types de vulnérabilités dans
notre code et à les corriger lorsqu’elles sont découvertes.
1-Assurer une bonne gestion des privilèges
utilisateurs
Les logiciels SGBD permette de créer des utilisateurs avec des permissions très
fines. Nous devons nous assurer que l’utilisateur qui interroge la base de
données ne dispose que des permissions minimales.
2-Utiliser des requêtes préparées
L’utilisation de requêtes paramétrées est un autre moyen de s’assurer que
l’entrée est sécurisée. Les requêtes paramétrées contiennent des espaces réservés
aux données d’entrée, qui sont ensuite échappées et transmises par les pilotes.
Au lieu de transmettre directement les données dans la requête SQL, nous
utilisons des espaces réservés et les remplissons ensuite avec des fonctions PHP.
La requête est modifiée pour contenir deux espaces réservés, marqués par des
« ? » où le nom d’utilisateur et le mot de passe seront placés. Nous lions ensuite
le nom d’utilisateur et le mot de passe à la requête en utilisant la
fonction mysqli_stmt_bind_param(). Cette fonction permet d’échapper aux
guillemets et de placer les valeurs dans la requête.