Injection SQL : comprendre et se protéger
Auteur : Rédaction Domeny.ForSale Publié le
En bref : l’injection SQL est une vulnérabilité qui permet de modifier le comportement d’une requête de base de données à l’aide de données d’entrée spécialement conçues. Elle apparaît surtout lorsque l’application construit des commandes SQL en y concaténant du texte fourni par l’utilisateur. La protection de base consiste à utiliser des requêtes paramétrées, qui séparent la commande de ses données.
Comment les données de l’utilisateur deviennent une commande
SQL est un langage permettant de lire et de modifier les données des bases de données relationnelles. Une application peut l’utiliser pour rechercher un compte, récupérer une commande ou enregistrer la nouvelle adresse d’un client. Une injection SQL se produit lorsque les données d’entrée sont interprétées comme faisant partie de la syntaxe d’une commande SQL.
Supposons que l’application recherche un compte à partir de son identifiant de connexion. Le développeur écrit le début de la requête, y ajoute la valeur du formulaire, puis ferme cette valeur par une apostrophe. Un identifiant ordinaire donne le résultat attendu, mais un texte contenant une apostrophe et une condition supplémentaire peut modifier le sens de toute la recherche.
La valeur dangereuse ne provient pas nécessairement d’un formulaire de connexion. Elle peut aussi venir d’un paramètre d’URL, d’un filtre de produits, d’un en-tête de requête ou de données déjà enregistrées dans la base. Ce qui compte, c’est la manière dont l’application utilise cette valeur, et non sa provenance.
Une expérience simple sous Linux
L’exemple ci-dessous fonctionne uniquement sur une base temporaire située dans la mémoire du processus. Il ne se connecte à aucun site ni serveur externe. Il illustre la lecture d’enregistrements qui ne devraient pas être renvoyés, et non un mécanisme de connexion complet.
Vous avez besoin de Python 3 avec le module sqlite3 disponible. Pour vérifier sa présence, exécutez cette commande dans le terminal :
python3 -c "import sqlite3; print(sqlite3.sqlite_version)"
Si la commande échoue, consultez la documentation de votre distribution pour savoir comment installer Python 3 ou le module manquant. Il n’est pas nécessaire de démarrer un serveur de base de données distinct.
Exécutez le bloc entier dans le terminal :
python3 - <<'PY'
import sqlite3
connexion = sqlite3.connect(":memory:")
polaczenie.execute(
"CREATE TABLE comptes (id INTEGER PRIMARY KEY, identifiant TEXT)"
)
polaczenie.executemany(
"INSERT INTO comptes (identifiant) VALUES (?)",
[("anne",), ("pierre",)]
)
valeur = "' OR 1=1 -- "
requete = "SELECT id, identifiant FROM comptes WHERE identifiant = '" + valeur + "'"
print("Requête :", requete)
print("Résultat :", polaczenie.execute(requete).fetchall())
polaczenie.close()
PY
Le résultat contient les deux comptes, alors qu’aucun n’a d’identifiant correspondant à la valeur fournie. L’apostrophe ferme le texte de l’identifiant, OR 1=1 ajoute une condition toujours vraie et -- ouvre un commentaire : l’apostrophe finale n’a donc plus d’effet sur la requête.
Ce code est volontairement vulnérable et destiné à une démonstration locale. Ne testez pas de telles entrées sur les services d’autrui sans autorisation explicite précisant le périmètre des tests.
Comment corriger ce même code
Une requête paramétrée contient des marqueurs à la place des données ; les valeurs sont transmises séparément à la bibliothèque qui gère l’accès à la base. Dans le module sqlite3, ? peut servir de marqueur. La bibliothèque traite la valeur transmise comme une donnée, et non comme un élément supplémentaire de syntaxe SQL.
Dans l’exemple précédent, remplacez la construction et l’exécution de la requête par cet extrait, en le laissant avant polaczenie.close() :
requete = "SELECT id, identifiant FROM comptes WHERE identifiant = ?"
resultat = polaczenie.execute(requete, (valeur,)).fetchall()
print("Résultat :", resultat)
Vous obtenez maintenant une liste vide : la base recherche un identifiant correspondant littéralement à l’ensemble du texte transmis. Si vous remplacez la valeur de valeur par "anne", la requête ne renverra que le compte d’Anne. La virgule dans (valeur,) crée un tuple à un seul élément, attendu par ce mode de transmission des paramètres.
Ne remplacez pas les paramètres par une suppression manuelle des apostrophes ou par du formatage de texte, par exemple avec une f-string. Un identifiant contenant une apostrophe peut être une donnée valide ; c’est à l’application de le traiter en toute sécurité. La syntaxe des paramètres dépend de la bibliothèque : n’utilisez donc pas le marqueur ? avec n’importe quel pilote sans vérification préalable.
Quelles peuvent être les conséquences de cette vulnérabilité ?
Une injection SQL peut permettre de lire les données d’autrui, de contourner une partie des contrôles d’accès, de modifier des enregistrements ou de les supprimer. Les conséquences réelles dépendent de la requête, du moteur de base de données et des droits du compte utilisé par l’application. La découverte d’une vulnérabilité ne signifie pas automatiquement que tout le serveur peut être compromis.
L’absence d’erreur visible ne prouve pas non plus que l’application est sûre. Celle-ci peut masquer les messages de la base, et seul un écart entre les réponses peut révéler un comportement modifié. À l’inverse, une erreur isolée après la saisie d’une apostrophe est un signal à analyser, et non une preuve suffisante d’injection SQL.
Que vérifier dans votre propre application ?
Commencez par examiner les endroits où sont construites les requêtes SQL :
- Remplacez la concaténation de texte par des paramètres pour toutes les valeurs provenant de l’extérieur de la requête.
- Vérifiez aussi les requêtes brutes lorsque vous utilisez un ORM, c’est-à-dire une couche qui fait correspondre les objets de l’application aux données de la base.
- Choisissez les noms de colonnes et le sens du tri dans une liste fermée d’options autorisées. Les paramètres de valeurs ne peuvent généralement pas remplacer ces éléments de syntaxe.
- Limitez les droits du compte de base de données aux opérations nécessaires à l’application.
- N’affichez pas aux utilisateurs les erreurs détaillées de la base ni les requêtes complètes contenant des données confidentielles.
Pour commencer, choisissez une recherche qui utilise des données fournies par l’utilisateur. Ajoutez un test avec une valeur ordinaire et un autre avec un texte contenant une apostrophe. Vérifiez que les deux entrées restent traitées comme des données, sans modifier la structure de la requête.
À lire aussi : Qu’est-ce qu’un ETF ?
