Sécurité

Un captcha sans dépendance : HMAC et une addition

2026-02-04 · Datacampus

La plupart des sites utilisent reCAPTCHA de Google pour protéger leurs formulaires. C'est efficace, mais c'est aussi un script externe de 300 Ko, un appel réseau vers les serveurs de Google, un cookie tiers, et une énigme visuelle pénible pour l'utilisateur.

On s'est demandé : est-ce qu'on peut faire plus simple ?

Le problème des captchas classiques

Les captchas basés sur la session (le serveur stocke la bonne réponse en $_SESSION, l'utilisateur répond, le serveur compare) posent plusieurs problèmes :

  • Onglets multiples — si l'utilisateur ouvre le formulaire dans deux onglets, le second écrase la réponse du premier. La soumission du premier onglet échoue systématiquement.
  • Expiration de session — si l'utilisateur met du temps à remplir le formulaire, la session peut expirer. La réponse stockée disparaît.
  • Load balancers — derrière un répartiteur de charge, la requête GET et la requête POST peuvent atterrir sur deux serveurs différents avec des sessions distinctes.
  • Proxy et cache — certains reverse proxies ou CDN peuvent interférer avec les cookies de session.

Tous ces scénarios mènent au même résultat : l'utilisateur saisit la bonne réponse, mais le serveur la rejette.

Une solution : HMAC + addition

L'idée est simple : ne rien stocker côté serveur. La vérification est entièrement contenue dans le formulaire lui-même.

1. Génération du formulaire

Quand la page de contact se charge, le serveur :

  1. Génère deux nombres aléatoires (par exemple 7 et 4)
  2. Calcule la bonne réponse (11)
  3. Signe cette réponse avec un HMAC-SHA256 en utilisant le token CSRF comme clé
  4. Affiche la question à l'utilisateur et place le hash dans un champ caché
// Génération (PHP)
$a = random_int(2, 12);
$b = random_int(1, 10);
$answer = $a + $b;
$hmac = hash_hmac('sha256', (string)$answer, $csrf_token);

Le formulaire HTML contient alors :

<!-- Visible -->
<label>Combien font 7 + 4 ?</label>
<input type="number" name="captcha">

<!-- Caché -->
<input type="hidden" name="captcha_hmac" value="a1b2c3...">

2. Vérification à la soumission

À la réception du formulaire, le serveur :

  1. Récupère la réponse saisie par l'utilisateur
  2. Recalcule le HMAC de cette réponse avec la même clé (le token CSRF, toujours en session)
  3. Compare les deux signatures avec hash_equals()
// Vérification (PHP)
$input = trim($_POST['captcha']);
$hmac_received = $_POST['captcha_hmac'];
$expected = hash_hmac('sha256', $input, $csrf_token);

if (!hash_equals($hmac_received, $expected)) {
    // Mauvaise réponse
}

Si l'utilisateur répond 11, le HMAC de « 11 » correspond au hash stocké dans le formulaire. Si un bot répond 42, le HMAC ne correspond pas. Simple.

Pourquoi ça tient la route

Pas de dépendance externe

Zéro appel réseau, zéro script tiers, zéro cookie supplémentaire. Tout tient en quelques lignes de PHP natif.

Pas de session pour le captcha

La réponse n'est jamais stockée sur le serveur. Onglets multiples, session expirée, load balancer : aucun impact.

Infalsifiable

Sans connaître le token CSRF (stocké en session, jamais exposé dans l'URL), un attaquant ne peut pas forger un HMAC valide pour une réponse arbitraire.

Respectueux de l'utilisateur

Pas de puzzle visuel, pas de « cliquez sur les feux tricolores ». Une addition à deux chiffres, c'est rapide et accessible.

Les limites, en toute transparence

Ce captcha ne prétend pas arrêter une attaque ciblée. Un bot spécifiquement programmé pour notre formulaire pourrait parser la question, calculer la réponse et soumettre le formulaire. Mais en pratique :

  • Les bots génériques (ceux qui spamment des milliers de formulaires) ne savent pas résoudre une addition affichée en texte dans un <label>.
  • Le honeypot (champ invisible) bloque une grande partie du spam automatisé.
  • Le token CSRF empêche la soumission depuis un site tiers.

Ces trois couches combinées suffisent largement pour un formulaire de contact d'entreprise. Et si un jour le spam devenait un problème, on pourrait ajouter un rate limiting côté serveur sans toucher au captcha.

En résumé

Avant : un captcha basé sur $_SESSION qui cassait dès que la session était perdue.

Après : un HMAC de 4 lignes de PHP, zéro dépendance, zéro service tiers, et un formulaire qui marche à tous les coups.

Parfois la meilleure solution est la plus simple. Pas besoin de Google, pas besoin de npm, pas besoin de 300 Ko de JavaScript. Juste une addition et une signature cryptographique.

— Datacampus

Hébergement souverain, éco-responsable et infogéré

Serveurs en France, énergie renouvelable, support humain. Découvrez ce que Datacampus peut faire pour vous.

Découvrir nos solutions Nous contacter

Articles sur le même sujet

← Retour au blog