Qu'est-ce qu'une revue de code de sécurité ?
Une revue de code de sécurité consiste à relire le code d'un autre, ou le sien, en cherchant ce qui peut mal tourner. On suit une liste de questions plutôt que son intuition.
C'est l'un des moyens les moins chers de trouver une faille, car elle est trouvée avant la mise en ligne. Une relecture de dix minutes évite parfois un incident de dix jours.
À la fin de cette leçon
Remarque
Je peux relire un morceau de code avec une liste de contrôle et signaler les lignes à risque.
Mots-clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Revue de code | la relecture du code par une autre personne avant qu'il soit accepté | |
| Liste de contrôle | une liste de questions posées à chaque relecture, dans le même ordre | |
| Diff | l'affichage des lignes retirées et ajoutées par une modification | + ligne ajoutée |
| Faux positif | une ligne signalée à tort, qui est en réalité sûre | |
| Journal | l'historique écrit de ce que fait le programme, pour retrouver ce qui s'est passé | |
| Message d'erreur | le texte affiché quand quelque chose échoue |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
La règle unique
Attention
Ce laboratoire est une boutique volontairement fragile. Lisez 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.
Une liste de contrôle remplace l'intuition
Sans liste, on regarde ce qui attire l'œil et on oublie le reste. Avec une liste, on pose les mêmes questions à chaque fois, même fatigué.
Chaque question correspond à une leçon de ce module. Pas besoin de tout savoir par cœur : on lit le code ligne par ligne et on coche.
Exemple travaillé : la liste de contrôle
| Question | Ce qui doit alerter | Leçon |
|---|---|---|
| D'où vient la donnée ? | Une saisie utilisée sans contrôle | Valider les données |
| Comment la requête est-elle écrite ? | Un + ou une mise en forme qui colle du texte | Requêtes paramétrées |
| Où sont les secrets ? | Un mot de passe ou une clé écrits dans le fichier | Dépendances et secrets |
| Comment sont gardés les mots de passe ? | Du texte clair, ou un hachage rapide sans sel | Mots de passe et sessions |
| Que montre une erreur ? | Le texte brut de l'erreur de la base ou une trace | Conception sécurisée |
| Reste-t-il une trace ? | Un journal éteint ou sans date | Conception sécurisée |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Deux lignes du laboratoire à relire
Appliquons la liste à la boutique de votre laboratoire. Deux questions suffisent pour commencer : que montre une erreur, et reste-t-il une trace de ce qui se passe ?
Les deux commandes suivantes affichent chacune une ligne du programme, avec son numéro. Elles ne font que lire. Vous jugerez ensuite si chaque ligne passe la liste.
La ligne qui répond en cas d'erreur
docker exec app grep -n "database error" /tmp/app/app.pyAffiché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 s'occupe du journal
docker exec app grep -n "def log_message" /tmp/app/app.pyAffiché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 ces deux lignes montrent
La première affiche database error: suivi du texte brut de l'erreur de la base. Un visiteur lit donc des détails sur la construction du site. Un message utile doit rester simple, et le détail doit aller dans un journal.
La seconde est la fonction qui écrit le journal d'accès. Regardez la ligne qui la suit dans le fichier : le programme l'a vidée. Plus rien n'est écrit, donc plus de trace à relire après un incident.
Lire un diff, ligne par ligne
On relit rarement un fichier entier. On relit un diff, c'est-à-dire les lignes retirées, marquées par un moins, et les lignes ajoutées, marquées par un plus.
Posez-vous deux questions sur chaque ligne ajoutée : que reçoit-elle de l'extérieur, et que peut-elle faire d'irréversible ? Une ligne retirée compte aussi : un contrôle supprimé est une porte ouverte.
Un diff à relire avec la liste
def chercher(curseur, titre):
- curseur.execute("SELECT * FROM livres WHERE titre = ?", (titre,))
+ curseur.execute("SELECT * FROM livres WHERE titre = '" + titre + "'")
def lire_cle():
+ cle = "sk_test_ABCDEF123456"
+ return cle
def verifier(utilisateur):
- if utilisateur.est_admin and utilisateur.session_valide:
+ if utilisateur.session_valide:
Ce que la liste trouve dans ce diff
Trois problèmes. La requête paramétrée a été remplacée par du texte collé avec + : l'injection est de retour. Une clé d'accès a été écrite dans le fichier. Et le contrôle est_admin a été supprimé : n'importe quelle session valable passe.
Le dernier est le plus discret, car la ligne retirée n'apparaît dans aucun fichier final. Seul le diff le montre.
Une erreur fréquente
Attention
Beaucoup pensent qu'une revue cherche des fautes de style. La sécurité regarde autre chose : ce que le code fait de ce qu'il reçoit. Un code bien écrit, bien aligné et bien commenté peut avoir une faille. Une revue de sécurité se fait avec la liste, pas avec le goût.