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

Empêcher l'injection SQL avec des requêtes paramétrées

Après cette leçon: Je peux écrire une requête où aucune donnée venue de l'extérieur n'entre dans le texte de la requête.

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

Qu'est-ce qu'une requête paramétrée ?

Une requête paramétrée est une requête SQL où les valeurs sont envoyées à part, au lieu d'être collées dans le texte. La base de données sait alors toujours ce qui est la requête et ce qui est une valeur.

C'est la méthode qui arrête l'injection SQL. Vous allez apprendre à l'écrire, à la reconnaître dans le code d'un autre, et à construire une requête à critères variables sans rien risquer.

À la fin de cette leçon

Remarque

Je peux écrire une requête où aucune donnée venue de l'extérieur n'entre dans le texte de la requête.

Mots-clés

MotCe que ça veut direExemple
Requête paramétréeune requête dont les valeurs voyagent séparément du texteWHERE titre = ?
Marqueurle signe qui tient la place d'une valeur dans la requête?
Injection SQLun texte venu de l'extérieur qui change le sens de la requête
Échappementle fait de neutraliser à la main les signes spéciaux d'un texte
Liste permisela liste des mots acceptés pour une partie qui ne peut pas être un marqueur
Compte de la basel'identité sous laquelle le programme se connecte à la base

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

La règle unique

Attention

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

Coller du texte dans une requête est dangereux

Un programme qui écrit sa requête avec un signe + mélange deux sortes de texte : le sien et celui du visiteur. La base reçoit une seule chaîne et ne sait plus les séparer.

Le texte du visiteur peut alors se faire passer pour une partie de la requête. Ce n'est pas une question de talent de l'attaquant, c'est un défaut de construction.

Deux façons d'écrire la même recherche

Code: python
import sqlite3
base = sqlite3.connect(":memory:")
base.execute("CREATE TABLE livres (titre TEXT, auteur TEXT)")
base.execute("INSERT INTO livres VALUES (?, ?)", ("L'Étranger", "Camus"))
base.execute("INSERT INTO livres VALUES (?, ?)", ("La Peste", "Camus"))
mot = "Peste"
# Mauvaise : le mot est collé dans le texte avec +.
collee = "SELECT titre FROM livres WHERE titre LIKE '%" + mot + "%'"
# Bonne : le mot voyage à part, à la place du ?.
propre = "SELECT titre FROM livres WHERE titre LIKE ?"
print(base.execute(collee).fetchall())
print(base.execute(propre, ["%" + mot + "%"]).fetchall())

Échapper à la main ne suffit pas

On pourrait doubler chaque apostrophe du texte tapé avant de le coller. C'est de l'échappement. Il marche parfois et échoue souvent : un seul signe oublié, un seul codage de caractères inattendu, et la porte s'ouvre.

Un marqueur n'a pas ce problème. Il ne transforme pas le texte, il l'empêche d'être lu comme de la requête.

Reconnaître une requête paramétrée dans du code

Lire du code est la moitié du métier. Une requête sûre a des ? à la place des valeurs, et une liste de valeurs à côté. Une requête fragile a un + ou un % qui colle une variable.

Le programme de votre laboratoire contient les deux sortes. Lancez ces deux commandes, qui regardent seulement : la première cherche la fonction de recherche, la seconde les endroits qui écrivent plusieurs lignes d'un coup.

La ligne où commence la recherche

Commande
docker exec app grep -n "def search" /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.

Les endroits qui utilisent déjà des marqueurs

Commande
docker exec app grep -n "executemany" /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.

Le programme sait déjà écrire des marqueurs

La première commande affiche 27:def search(term):. La seconde affiche deux lignes, la 15 et la 20, où le programme écrit ses propres produits et ses comptes avec des ?.

Ces lignes sont sûres. Cherchez maintenant la ligne de la fonction de recherche : elle n'a pas de ?. Voilà la différence à repérer en revue de code.

Un marqueur remplace une valeur, pas un mot

Un marqueur peut tenir la place d'un nombre ou d'un texte. Il ne peut pas tenir la place d'un nom de colonne, d'une table ou d'un sens de tri.

Pour ces parties, on écrit le mot soi-même, mais seulement après l'avoir comparé à une liste permise. Le visiteur choisit parmi vos mots, il n'écrit jamais les siens.

Exemple travaillé : une requête construite par morceaux

MorceauD'où vient-il ?Comment l'ajouter ?
SELECT titre FROM livres WHERE disponible = 1Écrit par le développeurTexte fixe
AND auteur = ?Écrit par le développeur, quand un auteur est demandéTexte fixe, valeur dans la liste
L'auteur lui-mêmeSaisi par le visiteurDans la liste des valeurs, jamais dans le texte
ORDER BY titreChoisi parmi deux mots permisMot de la liste permise, jamais le texte du visiteur

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

Limiter les droits du compte de la base

Même bien écrit, un programme peut contenir une faille qu'on n'a pas vue. La seconde ligne de défense est le compte sous lequel il parle à la base.

Un site de recherche n'a besoin que de lire quelques tables. S'il ne peut ni écrire ni lire les comptes des clients, une injection réussie rapporte beaucoup moins. C'est le moindre privilège, appliqué à la base.

Une erreur fréquente

Attention

Beaucoup pensent qu'un format ressemblant à une requête paramétrée en est une. Par exemple, une phrase écrite avec la mise en forme de Python, f"... {mot}", colle le texte tout autant qu'un plus. Un marqueur n'est vrai que si la valeur part dans la liste à côté.

À 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.