(Contact / #373) Anti-spam du formulaire : honeypot, jeton signé, flag suspect #374

Merged
mlvtito merged 1 commit from contact-antispam into main 2026-11-13 21:12:47 +00:00
Collaborator

Closes #373

Implémente le combo validé : option 1 (honeypot) + option 2 (jeton signé + délai minimum) + option 5 (flag au lieu de rejet). ~100 lignes, zéro service externe, zéro infra.

Fonctionnement

contact.php

  • GET renvoie un jeton "<timestamp>.<hmac(timestamp)>" (HMAC SHA-256 avec CONTACT_SECRET, qui ne quitte jamais le serveur)
  • POST : le jeton est vérifié (signature, délai ≥ 5 s, âge ≤ 24 h). S'ajoutent le honeypot (website) et une heuristique « plus de 3 liens dans le message »
  • Aucun rejet : une soumission suspecte part quand même dans Forgejo, avec préfixe [suspect] dans le titre, les raisons listées en bas du body, et le label spam si configuré. Le vrai message n'est jamais perdu, et le bot reçoit la même redirection « succès » (il ne sait pas qu'il a été flaggé)
  • si CONTACT_SECRET est absent : mode dégradé sans rejet — tout est flaggé suspect, rien n'est perdu

templates/contact.html

  • champ honeypot caché (CSS position: absolute; left: -9999px, tabindex="-1" — pas focusable au clavier)
  • <input type="hidden" name="token"> + 10 lignes de JS inline qui récupèrent le jeton au chargement de la page

⚠️ À faire côté OVH (config/secrets.php, hors repo)

'CONTACT_SECRET' => '<chaîne aléatoire longue>',   // requis — générer par ex. avec `openssl rand -hex 32`
'CONTACT_SPAM_LABEL_ID' => <id>,                   // optionnel — créer un label "spam" dans le repo cible et mettre son id

Sans ces clés, le site continue de fonctionner (tout est flaggé [suspect]), mais autant les poser avant le tag de déploiement.

Vérifié

  • php -l OK (PHP 8.5 local, code compatible 8.1+)
  • 12 assertions end-to-end sur serveur PHP local avec endpoint factice : jeton émis (GET), POST valide → payload propre + labels normaux, honeypot rempli → [suspect] + labels spam, jeton absent / trop rapide (< 5 s) / expiré (> 24 h) / signature falsifiée → [suspect] avec la bonne raison, 4 liens → [suspect], 3 liens → propre
  • Parcours navigateur complet : honeypot invisible et non focusable, jeton auto-rempli par le JS, formulaire rempli « humainement » → redirection #contact-success, alerte affichée, payload reçu sans flag
  • zola build + CI build : verte sur 1165165
Closes #373 Implémente le combo validé : **option 1 (honeypot) + option 2 (jeton signé + délai minimum) + option 5 (flag au lieu de rejet)**. ~100 lignes, zéro service externe, zéro infra. ## Fonctionnement **`contact.php`** - `GET` renvoie un jeton `"<timestamp>.<hmac(timestamp)>"` (HMAC SHA-256 avec `CONTACT_SECRET`, qui ne quitte jamais le serveur) - `POST` : le jeton est vérifié (signature, délai ≥ 5 s, âge ≤ 24 h). S'ajoutent le honeypot (`website`) et une heuristique « plus de 3 liens dans le message » - **Aucun rejet** : une soumission suspecte part quand même dans Forgejo, avec préfixe `[suspect]` dans le titre, les raisons listées en bas du body, et le label spam si configuré. Le vrai message n'est jamais perdu, et le bot reçoit la même redirection « succès » (il ne sait pas qu'il a été flaggé) - si `CONTACT_SECRET` est absent : mode dégradé sans rejet — tout est flaggé suspect, rien n'est perdu **`templates/contact.html`** - champ honeypot caché (CSS `position: absolute; left: -9999px`, `tabindex="-1"` — pas focusable au clavier) - `<input type="hidden" name="token">` + 10 lignes de JS inline qui récupèrent le jeton au chargement de la page ## ⚠️ À faire côté OVH (config/secrets.php, hors repo) ```php 'CONTACT_SECRET' => '<chaîne aléatoire longue>', // requis — générer par ex. avec `openssl rand -hex 32` 'CONTACT_SPAM_LABEL_ID' => <id>, // optionnel — créer un label "spam" dans le repo cible et mettre son id ``` Sans ces clés, le site continue de fonctionner (tout est flaggé `[suspect]`), mais autant les poser avant le tag de déploiement. ## Vérifié - `php -l` OK (PHP 8.5 local, code compatible 8.1+) - **12 assertions end-to-end sur serveur PHP local** avec endpoint factice : jeton émis (GET), POST valide → payload propre + labels normaux, honeypot rempli → `[suspect]` + labels spam, jeton absent / trop rapide (< 5 s) / expiré (> 24 h) / signature falsifiée → `[suspect]` avec la bonne raison, 4 liens → `[suspect]`, 3 liens → propre - **Parcours navigateur complet** : honeypot invisible et non focusable, jeton auto-rempli par le JS, formulaire rempli « humainement » → redirection `#contact-success`, alerte affichée, payload reçu sans flag - `zola build` + CI `build` : verte sur 1165165
(Contact / #373) anti-spam: honeypot, signed token and suspect flag
All checks were successful
/ build (push) Successful in 36s
/ publish (push) Has been skipped
11651658f9
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
serious-devs/website!374
No description provided.