# Une protection que je n'ai pas vue refuser n'existe pas

Langue : fr

Identifiant : article:controle-negatif

Noa Matout

J'ai construit un site privé, puis j'ai essayé de prouver qu'il fuyait : deux de mes protections ne tenaient pas.

J'ai construit un site destiné à trois personnes, pour une durée d'une semaine.
Tout le contenu devait rester derrière un mot de passe, y compris les images et
la vidéo. Rien ne devait subsister après la date limite.

Les besoins étaient donc inversés par rapport à un site normal. Je ne cherchais
pas de l'audience, je cherchais l'absence de fuite. Et une absence, ça ne se
constate pas en regardant le site fonctionner.

## Le principe que j'ai fini par adopter

Une vérification qui réussit toujours ne vérifie rien.

Quand j'écris un contrôle, je commence par lui donner une raison d'échouer.
J'introduis volontairement le défaut qu'il est censé détecter, je vérifie qu'il
le détecte, puis je retire le défaut. Si le contrôle passe au vert dans les deux
cas, il ne sert à rien, et il est pire qu'inutile : il me rassure.

J'ai appliqué ce principe à deux endroits. Les deux fois, j'ai trouvé un défaut
réel.

## Premier contrôle : rien du contenu ne doit atteindre le navigateur

Mes textes vivent dans des fichiers typés, importés uniquement par des composants
rendus côté serveur. En théorie, aucune phrase ne peut se retrouver dans le
JavaScript envoyé au navigateur. En théorie.

J'ai écrit une vérification en deux parties. La première parcourt le graphe
d'imports et échoue si un composant marqué client remonte jusqu'à un fichier de
contenu. La seconde cherche le texte lui-même dans les fichiers compilés.

Pour la tester, j'ai injecté une fuite dans une page, puis lancé la vérification.
Elle a échoué, comme prévu. J'ai ensuite injecté une fuite partielle : non pas un
texte entier, mais une phrase prise au milieu d'un paragraphe.

La vérification est passée au vert.

Elle cherchait des chaînes complètes. Une phrase isolée, une citation, une
troncature, tout cela lui échappait. Je l'ai réécrite pour chercher aussi des
phrases entières et des fenêtres glissantes de six mots consécutifs, plus les
variantes encodées que produit la compilation. J'ai rejoué les deux injections :
les deux sont détectées.

Sans ce second essai, j'aurais eu une vérification verte et une fuite possible.
C'est exactement le scénario que je redoute, parce qu'il ne ressemble pas à une
panne.

## Deuxième contrôle : refuser ce qui vient d'ailleurs

Ma page de connexion refuse les requêtes venues d'un autre site. J'ai écrit le
contrôle, je l'ai testé en ligne de commande avec une origine étrangère : refusé.
Avec la bonne origine : accepté. Contrôle vert.

Puis j'ai ouvert un navigateur. Impossible de me connecter.

Mon site envoie un en-tête qui demande au navigateur de ne transmettre aucun
référent. C'est une bonne pratique, je l'avais mise pour éviter de révéler
l'adresse de mes pages. Mais avec cette consigne, le navigateur annonce une
origine vide au lieu de la vraie. Mon contrôle voyait donc une origine inconnue
et refusait tout le monde, alors que mes essais en ligne de commande, eux,
envoyaient sagement la bonne origine.

Ma vérification ne testait pas ce que fait un navigateur. Elle testait ce que
fait mon outil de test.

Je me suis appuyé sur un autre en-tête, que les navigateurs envoient et qui dit
si la requête vient du même site, en gardant l'origine comme solution de repli.
Puis j'ai refait les deux essais dans un vrai navigateur : le formulaire hébergé
ailleurs est refusé, le mien passe.

## Ce que ces erreurs ont en commun

Dans les deux cas, ma protection existait, mon test existait, et ils ne se
rencontraient pas.

Le premier test vérifiait une forme de fuite au lieu de vérifier la fuite. Le
second vérifiait le comportement d'un client qui n'est pas celui de mes
utilisateurs. Ni l'un ni l'autre n'aurait été trouvé en relisant le code, parce
que le code était correct par rapport à ce que je croyais tester.

J'en ai tiré trois habitudes que je garde.

J'écris le contrôle négatif avant le contrôle positif. Je fabrique d'abord le
défaut, je regarde le test échouer, et seulement ensuite je corrige. Un test qui
n'a jamais échoué sous mes yeux n'a pas de valeur démontrée.

Je teste avec le client réel. Une ligne de commande n'envoie pas les mêmes
en-têtes qu'un navigateur, ne gère pas les mêmes règles de cookies, et ne
subit pas les consignes que j'envoie moi-même. Pour tout ce qui touche à
l'authentification, je finis dans un navigateur.

Je préfère un contrôle bruyant à un contrôle élégant. Ma vérification de fuite
cherche des fenêtres de six mots, ce qui produit quelques fausses alertes quand
une tournure banale se retrouve dans une bibliothèque. Je préfère écarter trois
alertes à la main plutôt que manquer la seule qui compte.

## La limite que je connais

Ces vérifications prouvent l'absence de ce que je sais chercher. Elles ne
prouvent pas l'absence de fuite. Une donnée qui sortirait par un chemin auquel je
n'ai pas pensé passerait au vert sans difficulté.

C'est pour ça que je ne présente jamais un résultat vert comme une garantie. Je
le présente pour ce qu'il est : la liste de ce que j'ai essayé de casser, et qui
a tenu.

## Périmètre de la mesure

controls : 2 contrôles. Contrôles essayés sur mon site privé, chacun ayant révélé un défaut réel.

windowWords : 6 mots consécutifs. Taille des fenêtres glissantes utilisées dans ma recherche de fuites de texte.
