Files
la-voix-du-peuple/docs/SECURITE_ANTI_ABUS.md
billisdead 45edc1fa77 Licence EUPL-1.2 + hardening anti-abus
P1 — Licence :
- Ajout du fichier LICENSE (EUPL-1.2 complet)
- README mis à jour : section licence, table docs, vars d'environnement
- En-têtes EUPL ajoutés dans les fichiers sources principaux (Flask, React)

P2 — Hardening anti-abus :
- Rate limiting Redis-ready (REDIS_URL) avec clé fingerprint + IP
- Honeypot anti-bot : champ caché côté client + vérification serveur
- Fingerprinting non-PII via FingerprintJS (hash SHA-256, colonne ideas.fingerprint_hash)
- Cooldown session : cookie httpOnly signé HMAC-SHA256 (SECRET_KEY requis)
- Détection de flood : alerte WARNING si > FLOOD_THRESHOLD soumissions / 5 min
- hCaptcha stub : intégré, activable via HCAPTCHA_SECRET_KEY + VITE_HCAPTCHA_SITE_KEY
- Nouvelles dépendances : redis (backend), @fingerprintjs/fingerprintjs + @hcaptcha/react-hcaptcha (frontend)
- docs/SECURITE_ANTI_ABUS.md : documentation complète des seuils et de la configuration

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 18:05:46 +02:00

8.4 KiB

Sécurité anti-abus — La Voix du Peuple

Document technique décrivant les protections contre les attaques sybil, les floods de bots et les brigading coordonnés. Mis à jour : mai 2026.


Contexte et risque

La plateforme est conçue sans authentification (choix philosophique préservant l'anonymat). Cette absence rend triviale, sans protection, la manipulation des données par :

  • Sybil attacks : multiplication des soumissions depuis la même entité
  • Bot floods : soumissions automatisées en masse
  • Brigading coordonné : campagnes organisées pour inonder la synthèse de contenus orientés

Les protections suivantes traitent ce risque sans remettre en cause l'anonymat des contributeurs légitimes.


Couches de protection (ordre d'application)

1. Honeypot anti-bot

Principe : un champ de formulaire est présent dans le HTML mais rendu invisible aux utilisateurs réels (display: none, position: absolute, aria-hidden="true"). Les bots qui analysent le DOM et remplissent tous les champs déclenchent le honeypot.

Comportement :

  • Client : si le champ _hp a une valeur lors de la soumission, l'appel API n'est pas effectué. Réponse simulée silencieuse côté JS.
  • Serveur : si _hp est présent et non vide dans le corps JSON, le serveur retourne un 201 factice sans enregistrer quoi que ce soit (logger.info("Honeypot déclenché")).

Fichiers : artifacts/voix-du-peuple/src/pages/home.tsx · artifacts/flask-api/app.py


2. hCaptcha (stub — activer en production)

Principe : widget CAPTCHA humain présenté avant la soumission. hCaptcha est choisi pour :

  • Gratuit (tier communautaire)
  • RGPD-compliant (pas de cookies tiers, données UE)
  • Pas de dépendance à Google

État actuel : stub intégré, désactivé par défaut.

Pour activer :

  1. Créer un compte sur hcaptcha.com
  2. Créer un site, récupérer la clé de site et la clé secrète
  3. Configurer les variables d'environnement :
# Frontend (.env dans artifacts/voix-du-peuple/)
VITE_HCAPTCHA_SITE_KEY=votre-cle-de-site

# Backend (.env ou variable système)
HCAPTCHA_SECRET_KEY=votre-cle-secrete
  1. Reconstruire le frontend : pnpm build

Comportement quand activé :

  • Le widget hCaptcha s'affiche dans le formulaire avant le bouton "Contribuer"
  • Le bouton est désactivé tant que le CAPTCHA n'est pas validé
  • Le token est transmis dans l'en-tête X-HCaptcha-Token
  • Le backend vérifie le token via l'API hCaptcha (https://hcaptcha.com/siteverify)
  • Si la clé secrète n'est pas configurée côté serveur, la vérification est sautée (dégradation gracieuse)

Fichiers : artifacts/voix-du-peuple/src/pages/home.tsx · artifacts/flask-api/app.py (_verify_hcaptcha())


3. Rate limiting par IP + fingerprint

Outil : Flask-Limiter v3 avec stockage Redis (si REDIS_URL défini) ou mémoire (dev).

Seuils par défaut (configurables via variables d'environnement) :

Endpoint Limite par défaut Variable de contrôle
POST /api/ideas 5/min · 3/heure RATE_LIMIT_CONTRIBUTIONS
POST /api/ideas/:id/flag 3/min · 10/heure
POST /api/admin/login 10/min
Toutes routes 60/heure · 200/jour

Clé de rate limiting : priorité au fingerprint FingerprintJS (hashé), sinon IP. Empêche de contourner la limite en changeant d'IP si le fingerprint est reconnu.

Pour activer Redis :

REDIS_URL=redis://localhost:6379/0

Sans Redis, le rate limiting est en mémoire (reset au redémarrage — suffisant pour une instance unique).

Fichiers : artifacts/flask-api/app.py (get_fingerprint_key(), RATE_LIMIT_CONTRIBUTIONS)


4. Fingerprinting non-PII

Outil : @fingerprintjs/fingerprintjs v4 (open source, pas de compte requis).

Principe : FingerprintJS génère un visitorId côté client à partir de caractéristiques du navigateur (User-Agent, timezone, canvas fingerprint, etc.) sans créer de cookie tiers ni stocker de données personnelles.

Flux :

  1. À l'initialisation de l'app React (App.tsx), FingerprintJS est chargé
  2. Le visitorId est stocké en mémoire (non persisté)
  3. Il est envoyé sur chaque requête API dans l'en-tête X-Visitor-Id
  4. Le backend le hash en SHA-256 (32 premiers hex) avant tout stockage
  5. Le hash est enregistré en base (ideas.fingerprint_hash) pour analyse post-hoc si nécessaire

Données stockées : uniquement le hash SHA-256 tronqué — non-réversible, non-PII au sens du RGPD. Aucun cookie, aucun suivi cross-site.

Fichiers : artifacts/voix-du-peuple/src/App.tsx · lib/api-client-react/src/custom-fetch.ts · artifacts/flask-api/app.py · artifacts/flask-api/database.py


Principe : après une soumission acceptée, un cookie httpOnly signé HMAC-SHA256 est posé. Toute tentative de soumission avant l'expiration du cooldown est rejetée avec un 429.

Durée par défaut : 3600 secondes (1 heure), configurable :

CONTRIBUTION_COOLDOWN_SECONDS=3600

Signature : le cookie _cv contient {timestamp}.{signature} où la signature est HMAC-SHA256(SECRET_KEY, timestamp_bytes)[:16]. Impossible de forger sans connaître SECRET_KEY.

Prérequis :

SECRET_KEY=une-longue-chaine-aleatoire-minimum-32-chars

Si SECRET_KEY n'est pas défini, le cooldown est désactivé (dégradation gracieuse).

Limite : fonctionne pleinement en production (même domaine, Nginx reverse proxy). En développement cross-origin (Vite sur port différent de Flask), le cookie n'est pas envoyé automatiquement par le navigateur (CORS supports_credentials=False).

Fichiers : artifacts/flask-api/app.py (_sign_cooldown(), _verify_cooldown())


6. Détection de flood

Principe : compteur en mémoire par IP (et par fingerprint si disponible) sur une fenêtre glissante de 5 minutes. Si le seuil est dépassé, une alerte WARNING est émise dans les logs.

Seuil par défaut : 10 soumissions en 5 minutes, configurable :

FLOOD_THRESHOLD=10

Ce qui se passe : l'alerte est loggée mais la soumission n'est pas bloquée (le rate limiter Flask-Limiter s'en charge). L'objectif est d'alerter l'opérateur pour investigation.

Format de l'alerte :

WARNING ALERTE FLOOD — IP: 1.2.3.4 | fingerprint: abc123... | seuil: 10/5min

Pour aller plus loin : brancher sur un webhook (email Mailgun/Brevo, Slack/Mattermost) via un hook sur les logs WARNING avec le pattern ALERTE FLOOD.

Limite : l'état est en mémoire et se réinitialise au redémarrage. Pour une persistance cross-restart, utiliser Redis directement avec EXPIRE.

Fichiers : artifacts/flask-api/app.py (_check_flood(), _flood_tracker)


Variables d'environnement récapitulatif

# Rate limiting
REDIS_URL=redis://localhost:6379/0           # Optionnel — sinon mémoire
RATE_LIMIT_CONTRIBUTIONS=5 per minute;3 per hour  # Format flask-limiter

# Cooldown session
SECRET_KEY=une-longue-chaine-aleatoire-minimum-32-chars
CONTRIBUTION_COOLDOWN_SECONDS=3600

# Flood detection
FLOOD_THRESHOLD=10

# hCaptcha (désactivé si absent)
HCAPTCHA_SECRET_KEY=votre-cle-secrete        # Backend
VITE_HCAPTCHA_SITE_KEY=votre-cle-de-site     # Frontend (nécessite rebuild)

Ce que ces protections ne couvrent pas

  • Bots sophistiqués JavaScript-capable : FingerprintJS peut être contourné par un navigateur headless bien configuré. La combinaison IP + fingerprint + hCaptcha rend l'attaque coûteuse mais pas impossible.
  • VPN / Tor : le rate limiting IP peut être contourné. Le fingerprint compense partiellement.
  • Submissions manuelles coordonnées (brigading humain) : seul le contenu + la modération IA protège contre ce vecteur.
  • Cross-origin en dev : le cookie cooldown ne fonctionne pas en développement (ports différents, CORS sans credentials).

Évolutions futures recommandées

  1. Redis : déployer Redis et configurer REDIS_URL en production pour un rate limiting persistant et cross-process.
  2. hCaptcha : activer dès que la plateforme est ouverte au public (clé gratuite, 5 minutes de setup).
  3. Alerte flood automatique : brancher un webhook Brevo/Mailgun sur les logs ALERTE FLOOD.
  4. CAPTCHA invisible : envisager hCaptcha en mode "invisible" (score-based) pour ne pas imposer de défi aux utilisateurs légitimes.