fix(auth): retirer un CAPTCHA qui ne protegeait rien, filtrer autrement

SEC-AUTH-008. L addition a deux operandes entre 1 et 10 avait dix-neuf
reponses possibles et se resolvait en lisant la question comme une chaine.
Elle n arretait aucune inscription automatisee. Elle ajoutait en revanche
une etape a chaque personne, lecteur d ecran compris, contre une apparence
de protection — ce qui est pire que rien, puisque ca se compte comme une
protection.

L autre branche proposee par l audit etait un vrai service de CAPTCHA :
un tiers, une cle d API, une requete a chaque affichage, et un script
etranger remis dans script-src, defaisant le travail qui a ferme
SEC-WEB-001. Disproportionne pour le site d un club.

A la place, deux verifications invisibles pour un visiteur : un champ
piege, cache par la feuille de style et hors du parcours clavier, qu un
robot remplisseur complete et qu une personne ne voit jamais ; et un delai
minimal entre la remise du formulaire et son retour, l horodatage etant
dans la session signee et non dans un champ.

Le plafond est dit dans le code plutot que sous-entendu : ceci arrete le
pourriel de masse, pas quelqu un qui lit la page. Ce qui filtrerait
vraiment les inscriptions serait l activation des comptes par le staff —
is_active_account vaut True par defaut. C est une decision de produit.

L angle « session forgeable » du constat tombe : avec SECRET_KEY
compromise (SEC-001), on forge une session connectee sur n importe quel
compte et on n a aucune raison de s inscrire.

Au passage, ARCH-005 en partie : le bloc « regenerer, purger les mots de
passe, re-rendre » etait recopie quatre fois. Un seul helper, et la purge
des mots de passe ne peut plus etre oubliee dans la cinquieme copie.

Les refus sont journalises avec leur motif — c est le seul endroit ou un
abus du formulaire devient visible — mais restent indistinguables pour
l expediteur : nommer la regle indique comment la contourner.

429 tests.
This commit is contained in:
GGThed
2026-08-11 12:14:50 -04:00
parent d0a9e75fe6
commit d8541678a6
9 changed files with 823 additions and 556 deletions
+8 -3
View File
@@ -146,9 +146,14 @@
required>
</div>
<div class="form-group">
<label for="captcha_answer"><i class="fas fa-calculator"></i> {{ captcha.question }}</label>
<input type="number" id="captcha_answer" name="captcha_answer" placeholder="{{ _('Answer') }}" required autocomplete="off">
{# Honeypot (SEC-AUTH-008). Not a field anyone is meant to see or fill:
hidden in the stylesheet, kept out of the tab order, and told to
screen readers to skip. A submission that carries a value here is a
robot filling every input it can find.
Do not add a label, do not translate the name, do not remove
aria-hidden — each of those turns it into a trap for a person. #}
<div class="honeypot" aria-hidden="true">
<input type="text" name="{{ honeypot_field }}" tabindex="-1" autocomplete="off" value="">
</div>
<button type="submit" class="btn btn-primary btn-block">{{ _('Create Account') }}</button>