gdpr-captcha
v1.0.0
Published
Self-hosted GDPR-friendly CAPTCHA service with a lightweight client widget.
Maintainers
Readme
GDPR-Friendly Self-Hosted CAPTCHA
Un CAPTCHA auto‑hébergé, respectueux de la vie privée, facile à intégrer sur n’importe quel site. Aucune dépendance tierce, pas de tracking, pas de cookies par défaut.
- Vérification stateless via jetons signés (pas de stockage serveur de la solution)
- Défis en SVG (pas de dépendances natives)
- Honeypot + temps minimum de résolution (anti-bot)
- Widget client simple + pages de démo
Démarrage rapide (Windows PowerShell)
Créez un
.envdepuis.env.exampleet définissez unJWT_SECRETfort.Installez puis lancez:
Set-Location "c:\\Users\\leoba\\Downloads\\Capcha"
if (-Not (Test-Path .env)) { Copy-Item .env.example .env }
npm.cmd install
npm.cmd start- Ouvrez la démo: http://localhost:8787/demo/
Endpoints API
GET /api/captcha/init→{ token, svg, minMs }- Retourne un défi sous forme d’SVG et un jeton signé encapsulant le hash attendu
POST /api/captcha/verifybody{ token, answer, elapsedMs, honeypot }→{ ok, passToken? }- Vérifie la réponse; si correcte, renvoie un
passTokencourt‑terme
- Vérifie la réponse; si correcte, renvoie un
POST /api/captcha/validatebody{ passToken }→{ ok }- À appeler côté serveur de votre site avant d’accepter la soumission
Intégration sur vos sites
Incluez les assets du widget depuis votre serveur CAPTCHA (ou même origine):
<link rel="stylesheet" href="https://votre-domaine-captcha/captcha/widget.css">
<script src="https://votre-domaine-captcha/captcha/widget.js" defer></script>Montez le widget dans votre formulaire:
<form method="post" action="/submit">
<!-- … vos champs … -->
<div id="captcha"></div>
<button type="submit">Envoyer</button>
</form>
<script>
// Si votre CSP interdit l’inline JS, placez ce code dans un fichier .js séparé
GdprCaptcha.mount('#captcha', { endpoint: 'https://votre-domaine-captcha', locale: 'fr' });
//</script>Au submit, le widget ajoute un input caché captcha_pass contenant un passToken. Votre backend doit le valider avant tout traitement.
Validation côté serveur (exemples)
Node/Express (via l’endpoint de validation):
// Exemple de route Express dans VOTRE site
app.post('/submit', async (req, res) => {
const passToken = req.body.captcha_pass;
const r = await fetch('https://votre-domaine-captcha/api/captcha/validate', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ passToken })
});
const data = await r.json();
if (!data.ok) return res.status(400).send('Captcha invalide');
// Continuer le traitement
res.send('OK');
});PHP (cURL):
$passToken = $_POST['captcha_pass'] ?? '';
$ch = curl_init('https://votre-domaine-captcha/api/captcha/validate');
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['passToken' => $passToken]),
CURLOPT_RETURNTRANSFER => true,
]);
$out = curl_exec($ch);
$data = json_decode($out, true);
if (!isset($data['ok']) || !$data['ok']) {
http_response_code(400);
exit('Captcha invalide');
}
// Continuer le traitementPython (Django/Flask):
import requests
def validate_captcha(pass_token: str) -> bool:
r = requests.post('https://votre-domaine-captcha/api/captcha/validate', json={'passToken': pass_token}, timeout=5)
r.raise_for_status()
return r.json().get('ok', False)CORS et CSP
- Si votre site est sur un autre domaine, ajoutez son origine dans
.env→ALLOWED_ORIGINS=https://site1.com,https://site2.com - CSP recommandée (via Helmet) côté serveur CAPTCHA:
// Exemple: restreindre les scripts aux fichiers statiques (pas d'inline)
app.use(helmet.contentSecurityPolicy({
useDefaults: true,
directives: {
"script-src": ["'self'"],
"style-src": ["'self'", "'unsafe-inline'"],
"img-src": ["'self'", "data:"]
}
}));Durcir la détection anti-bot (mode pro)
Les recommandations suivantes rendent l’automatisation plus coûteuse tout en restant RGPD‑friendly:
- Limitation de débit côté edge/proxy
- NGINX (exemple):
limit_req_zone $binary_remote_addr zone=captcha:10m rate=30r/m;
server {
location /api/captcha/ { limit_req zone=captcha burst=10 nodelay; }
}- Node (Express):
const rateLimit = require('express-rate-limit');
const limiter = rateLimit({ windowMs: 60_000, max: 60 });
app.use('/api/captcha/', limiter);- Temps minimum et délais
- Augmentez
CAPTCHA_MIN_MS(ex: 3000–6000ms). Les scripts rapides échouent. - Introduisez, côté frontend, un petit délai progressif après plusieurs erreurs.
- Honeypots et champs dynamiques
- Le widget intègre un honeypot; ajoutez aussi un champ honeypot côté formulaire de votre site.
- Faites tourner les noms d’inputs (ex:
contact_phone→c_phone_923) et ignorez les soumissions sanscaptcha_pass.
- Validation stricte côté serveur de votre site
- Rejetez si: pas de
captcha_pass, token expiré, trop d’essais récents, user‑agent suspect. - Vérifiez l’en‑tête
Origin/Refererlorsque c’est pertinent.
- Variantes de défi (alterner régulièrement)
- Texte tordu (par défaut, via
svg-captcha). - Expression mathématique simple (ex: « 3 + 7 = ? ») rendue en SVG.
- Question‑réponse courte personnalisée (ex: « Couleur du ciel ? » → « bleu »).
Astuce: alternez aléatoirement le type de défi et le thème visuel (couleurs, bruit, police) pour éviter l’apprentissage automatique sur un seul format.
- Jetons et sécurité
- Faites tourner
JWT_SECRETpériodiquement. - Réduisez
PASS_TOKEN_TTL_S(ex: 5–10 min) selon vos besoins. - Envisagez de lier le
passTokenà un contexte serveur (ex: session interne) au moment de votre validation.
- Observabilité sans données perso
- Gardez des compteurs d’erreurs globales par minute (sans IPs) pour déclencher du throttling.
- Journalisez seulement les raisons techniques (ex:
too_fast,wrong_answer).
Accessibilité
- Entrée texte accessible clavier, ARIA live pour le statut.
- Prévoyez une alternative de contact pour les personnes ayant une déficience visuelle.
Configuration
.env:
PORT(défaut 8787) — port du serveurJWT_SECRET— secret fort requis pour signer les jetonsALLOWED_ORIGINS— origines autorisées par CORS (séparées par virgules)CAPTCHA_MIN_MS— temps minimal (ms) avant d’accepter la vérificationCAPTCHA_TOKEN_TTL_S— validité des jetons de défiPASS_TOKEN_TTL_S— validité des jetons de passageCAPTCHA_TYPES— types de défis autorisés (ex:digits,math). Par défaut:digits,mathpour éviter tout texte alphabétique.
Notes RGPD
- Aucun appel tiers; pas de tracking; pas de cookies par défaut.
- Données traitées: réponse saisie, chronométrage (ms), états de jetons.
- IP non stockées par défaut; appliquez la limitation au niveau du proxy.
- Voir
PRIVACY.md.
Licence
MIT
