Plus facile à lire avec un fond crème, une teinte, plus de contraste ou un texte plus grand ? Choisissez vos couleurs et votre texte, une fois pour toutes les pages.

Leçon gratuite

Injection SQL : quand le texte change la requête

Après cette leçon: Je peux corriger une requête pour que le texte tapé ne puisse plus la changer.

Leçon de 55 min3 commandesLab d’injection SQL : attaquer une recherche faible, corriger la requête et prouver la correction

Une recherche qui affiche une erreur

Une fromagerie en ligne propose une barre de recherche dans son catalogue. Un visiteur tape Mont d'Or, un fromage très connu.

La page répond par une ligne de mots étranges, venus de la machine qui est derrière. Le gérant parle d'un petit bogue. C'est le signe que la page laisse le visiteur écrire une partie de ses questions.

La règle unique

Attention

Ce laboratoire est une boutique volontairement fragile. Attaquez votre copie privée, et rien d'autre : elle n'a aucune sortie vers Internet. Attaquer un système qui n'est pas le vôtre, ou sans permission de test, est un délit dans presque tous les pays.

À la fin de cette leçon

Remarque

Je peux corriger une requête pour que le texte tapé ne puisse plus la changer.

Mots-clés

MotCe que ça veut direExemple
Base de donnéesun espace organisé où un programme range des fiches et les retrouve
Requêteune question qu'un programme pose à une base de donnéesSELECT nom FROM produits
Injection SQLun texte tapé qui change la requête envoyée par le programme
Paramètreune valeur envoyée à côté de la requête, jamais dedans?
Chaîne de caractèresun morceau de texte dans un programme, souvent appelé simplement chaîne"comte"
Entréetout ce qu'un visiteur tape ou envoie à une page

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Une requête fabriquée avec du texte

Beaucoup de programmes fabriquent une requête en collant un début fixe et le texte du visiteur. La base reçoit une seule longue chaîne. Elle ne sait pas quelle partie vient du développeur.

Si le texte contient une apostrophe, elle ferme trop tôt le texte du développeur. Ce qui suit est alors lu comme une partie de la requête.

La requête que le programme fabrique

Code: python
# Remplacez terme par "Mont d'Or" puis lancez le code.
terme = "comte"
debut = "SELECT nom, prix FROM produits WHERE nom LIKE '%"
requete = debut + terme + "%'"
print(requete)

Exemple travaillé : trois recherches

Le visiteur tapeLa requête reçue par la baseCe qui se passe
comte... LIKE '%comte%'Tout marche comme prévu
Mont d'Or... LIKE '%Mont d'Or%'L'apostrophe ferme le texte trop tôt : la requête est cassée
x' AND 1=2 --... LIKE '%x' AND 1=2 --%'Le visiteur a ajouté une condition à lui

Les cellules que les étudiants remplissent restent vides dans cet aperçu.

Regardez la boutique de votre laboratoire

Votre laboratoire contient une vraie boutique avec cette faiblesse. Démarrez-le, puis lancez ces trois commandes. Elles regardent seulement.

La première fait une recherche ordinaire. La deuxième envoie un nom avec une apostrophe, que la boutique ne sait pas traiter. La troisième affiche la ligne du code qui fabrique la requête.

Une recherche ordinaire

Commande
curl -s -G --data-urlencode "q=pen" http://app:8000/search

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Un nom avec une apostrophe

Commande
curl -s -G --data-urlencode "q=Baker's" http://app:8000/search

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

La ligne qui fabrique la requête

Commande
docker exec app grep -n "rows = db.execute" /tmp/app/app.py

Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.

Ce que disent les trois réponses

La première affiche Pen 1.2 : le nom et le prix. La deuxième affiche une ligne qui commence par database error. La boutique a collé votre apostrophe telle quelle dans sa requête.

La troisième affiche un numéro de ligne et une longue ligne de code. Repérez les signes + : chacun colle du texte tapé dans la requête.

Un paramètre garde le texte à part

Un paramètre règle le problème. Le programme envoie la requête avec un ? à la place de la valeur. Le texte tapé voyage séparément.

La base lit d'abord la requête et fixe sa forme. Le texte arrive ensuite et ne peut être qu'une valeur. Aucune apostrophe ne peut plus rien fermer.

La même recherche, avec un paramètre

Code: python
import sqlite3
base = sqlite3.connect(":memory:")
base.execute("CREATE TABLE produits (nom TEXT, prix REAL)")
base.execute("INSERT INTO produits VALUES (?, ?)", ("Mont d'Or", 14.5))
base.execute("INSERT INTO produits VALUES (?, ?)", ("Comté", 9.9))
# Changez terme et lancez le code : rien de ce que vous tapez ne ferme la requête.
terme = "Mont d'Or"
lignes = base.execute("SELECT nom, prix FROM produits WHERE nom LIKE ?", ("%" + terme + "%",)).fetchall()
print(lignes)

Une erreur fréquente

Attention

Beaucoup pensent qu'il suffit de retirer les apostrophes du texte tapé. C'est faux : un filtre oublie toujours un cas, et un cas oublié est une porte ouverte. Un paramètre protège parce qu'il ne mélange jamais le texte à la requête. Utilisez-en un pour chaque valeur.

Certaines parties ne sont pas des valeurs

Un paramètre remplace une valeur. Il ne remplace ni un nom de colonne ni un sens de tri. Une page qui laisse trier par prix ou par nom doit pourtant écrire ce mot dans la requête.

La méthode sûre est une courte liste de mots permis. Si le choix du visiteur n'y figure pas, on prend une valeur par défaut.

Seulement les mots de la liste

Code: python
PERMIS = ["nom", "prix"]
# Essayez choix = "prix", puis choix = "stock; vider", et lancez le code.
choix = "nom"
if choix in PERMIS:
    colonne = choix
else:
    colonne = "nom"
print("ORDER BY " + colonne)

Pour aller plus loin : le sens du tri

Une page de catalogue laisse souvent choisir aussi le sens du tri, croissant ou décroissant. Comme la colonne, ce mot ne peut pas être un paramètre.

Une fois le devoir réussi, copiez colonne_tri dans un bloc de code à vous. Écrivez sens_tri(choix), qui renvoie "ASC" ou "DESC" et "ASC" pour tout le reste. Essayez-la avec "desc", "DESC" et "DESC; vider", puis dites lesquels vous acceptez et pourquoi.

À propos de ce labo et de sa source: Lab d’injection SQL : attaquer une recherche faible, corriger la requête et prouver la correction

Vous enseignez à une classe ? Proposez ce module à vos étudiants avec un guide de l'enseignant, des devoirs corrigés et un laboratoire pour chaque étudiant.