Sécurité : Injection SQL

L'injection SQL consiste à falsifier des requêtes SQL afin d'opérer sur une base de données.

Icône de calendrier
Intermédiaire
4 chapitres

L’injection SQL, au même titre que la faille XSS et l’attaque CSRF, est considérée comme l’une des failles de sécurité les plus courantes. Il est, pour cela, important de comprendre en quoi elle consiste et être en mesure de s’en prémunir.

Comprendre l’injection SQL

Définition

L’injection SQL est une attaque qui consiste à falsifier des requêtes SQL afin de pirater une base de données.

Mais comment l’attaquant peut-il bien s’y prendre ? 🤔

Pour opérer sur la base de données cible, l’attaquant va saisir du code SQL malicieux dans des entrées utilisateurs (formulaires) de sorte à modifier la requête SQL qui sera exécutée en base de données.

Fonctionnement

Concrètement, une injection SQL s’écrit en terminant de manière prématurée l’exécution d’une requête SQL sur le serveur pour y écrire la sienne.

Cette nouvelle requête étant non prévue par le système, sa sécurité en devient compromise.

Cela se fait avec l’usage des caractères de commentaire :

  • Point-virgule ; (fin de requête)
  • Double-tiret -- ou dièse # (commentaire SQL)

Il existe de nombreux sous-types d’injections SQL, nous n’aborderons ici que certains aspects, l’objectif étant de comprendre la mécanique.

Comprendre par l’exemple

Pour bien comprendre l’injection SQL, prenons un exemple concret.

Admettons qu’un formulaire permette de s’authentifier sur un site web. Une base de données contiendrait la table users suivante :

Table users :

idusernamepassword
1adminadmin
2totoazerty

Le formulaire de connexion ressemblerait à ceci :

copié !
<h1>Connexion</h1>
<form method="POST">
	<div class="form-item">
		<label for="username">Login</label>
		<input type="text" name="username" id="username">
	</div>
	<div class="form-item">
		<label for="password">Mot de passe</label>
		<input type="password" name="password" id="password">
	</div>
	<input type="submit" value="Connexion">
</form>

Ne pas préciser d’attribut action dans la balise <form> indique que les données seront par défaut postées à la même URL. On imagine donc que sur la page la logique de connexion est incluse.

  1. Le serveur vérifie que les champs username et password sont saisis et valides.
  2. Une requête SQL sera alors exécutée sur la base de données afin de savoir si un utilisateur avec ce username existe bien dans la table users.

Cette requête ressemblerait à quelque chose comme :

SELECT * FROM users WHERE username = '{username_saisi}' AND password = '{password_saisi}';

{username_saisi} et {password_saisi} seraient bien entendu à remplacer par les variables contenant les champs saisis, concaténées dans le langage de programmation utilisé.

Injection SQL par commentaire

L’usage de commentaire nous permettrait de contourner la mécanique d’authentification de plusieurs manières.

Cela consiste à rendre la requête SQL valide et vraie, puis enfin, d’ignorer le code SQL restant.

Cas n°1 : on ne connaît que le username

Imaginons que nous connaissons un identifiant (ici « admin ») mais pas le mot de passe associé. Nous allons donc taper dans les champs :

ChampValeur
usernameadmin' -- (espace final important) ou admin' #
passwordPeu importe, par exemple azerty

La requête de connexion exécutée par le script sera donc :

copié !
SELECT * FROM users WHERE username = 'admin' -- ' AND password = 'azerty'
  1. L’apostrophe ' vient fermer la chaîne correspondante au login. Ce qui est écrit après l’apostrophe est donc considéré comme du SQL, plus comme le login à comparer.
  2. Le double tiret -- ou le hashtag # représentent le début d’un commentaire ; tout ce qui suit (ici, la vérification du mot de passe) n’est donc pas interprété.
Cas n°2 : on ne connaît ni username ni password

Imaginons maintenant que nous ne connaissons ni identifiant ni mot de passe. Nous allons néanmoins pouvoir remplir les champs de la sorte :

ChampValeur
usernamePeu importe, par exemple toto
password' or 1='1

La requête de connexion exécutée par le script sera donc :

copié !
SELECT * FROM users WHERE username = 'toto' AND password = '' or 1='1'
  1. L’apostrophe ' vient fermer la chaîne correspondante au mot de passe.
  2. L’instruction or 1='1 étant tout le temps vraie, tous les enregistrements de la table vont être renvoyés. On place un guillemet avant le 1, sinon le guillemet de la requête initiale (fermant le mot de passe) serait seul et il y aurait une erreur d’exécution.

Injection SQL par fin de requête

Le point-virgule ; permet de terminer une instruction SQL.

Il s’avère alors redoutable pour nous laisser la liberté d’exécuter notre propre requête SQL à la place de celle qui est normalement exécutée.

Saisir le mot de passe '; truncate table users; -- induirait le vidage complet de la table users en exécutant la requête SQL suivante :

copié !
SELECT * FROM users WHERE username = 'toto' AND password = ''; truncate table users; -- '

Risques de l’injection SQL

Les risques engendrés par une injection SQL sont très graves pour la simple et bonne raison que ce type de faille s’exécute directement sur le serveur de base de données et compromet le comportement des requêtes initiales.

Il est possible d’y écrire tout type de requête SQL comme des requêtes CRUD classiques (mais sur les BDD des autres !) ou de modifier le comportement de requêtes existantes :

  • Vol des données
  • Insertion de données
  • Modification de données
  • Suppression de données
  • Compromission d’une mécanique (bypasser une connexion…)

Se protéger de l’injection SQL

Se protéger d’une injection SQL consiste à échapper les caractères spéciaux potentiellement néfastes (', --, #…).

Aujourd’hui, cela se fait très simplement en écrivant toutes nos requêtes dynamiques (contenant des paramètres - des entrées utilisateur) sous forme de requêtes préparées.

Pour cela, chaque langage et/ou bibliothèque propose une implémentation propre de ces requêtes préparées (l’extension PDO de PHP, la librairie mysql pour JS, etc.).

En conclusion, l’injection SQL représente une menace sérieuse pour la sécurité des applications web et de leurs utilisateurs puisqu’elle opère directement sur le serveur de base de données. Il est essentiel de comprendre son fonctionnement et de mettre en place des mesures de protection adéquates pour minimiser les risques. L’utilisation de requêtes préparées est considérée comme la meilleure pratique pour contrer ce type d’attaque.