Créer un CAPTCHA mathématique en PHP : Coté serveur
Dans notre précédent article, « Créer un CAPTCHA mathématique en PHP : Coté client« , nous avons mis en place l’interface utilisateur permettant d’afficher dynamiquement un CAPTCHA sur un formulaire. Nous avons vu comment récupérer une image CAPTCHA et un token unique via une requête AJAX, ainsi que les mécanismes pour s’assurer que l’affichage s’adapte aux différentes tailles d’écran.
Dans cette seconde partie, nous allons maintenant nous focaliser sur une partie du traitement côté serveur. Nous allons voir comment :
- Générer une image CAPTCHA dynamiquement (
captcha_math.php
). - Gérer et sécuriser les tokens pour assurer l’unicité de chaque CAPTCHA (
captcha_tokens.php
). - Vérifier et valider la réponse envoyée par le formulaire (
validate_captcha.php
)
L’objectif est d’aboutir à un système de CAPTCHA complet et sécurisé, capable de prévenir les soumissions automatisées tout en restant simple à intégrer dans un projet PHP. Passons maintenant à l’implémentation de ces différentes étapes.
Mise en place et exécution du script captcha_math.php
Le script captcha_math.php
est le coeur du système. Il génère une image contenant une opération mathématique et la stocke temporairement côté serveur avec un identifiant unique (un token).
Avant de commencer la création de l’image, nous allons encapsuler tout le code dans un bloc try-catch
. Cela permet de capturer toute erreur pouvant survenir lors de la génération de l’image et d’afficher un message d’erreur approprié au lieu de casser l’affichage du formulaire.
try {
// le code qui va suivre devra être placé ici
} catch (Exception $e) {
// En cas d'erreur, loguez et affichez une erreur
error_log($e->getMessage());
header("Content-type: text/plain");
echo "Erreur lors de la génération de l'image.";
}
Génération de l’image CAPTCHA
Première étape : déterminer les dimensions de l’image. Celles-ci peuvent être passées en paramètres dans l’URL afin d’être adaptées dynamiquement en fonction des besoins de l’affichage. Cependant, si aucun paramètre n’est fourni, nous devons anticiper des valeurs par défaut pour garantir un affichage cohérent du CAPTCHA. De plus, il est essentiel de s’assurer que l’image générée ne soit ni trop grande, ni trop petite, afin de préserver une cohérence visuelle et d’utilisabilité, tout en évitant une surcharge inutile du serveur et des temps de chargement excessifs.
// Récupérer les dimensions depuis l'URL
$default_width = 150; // Valeur par défaut pour la largeur
$default_height = 50; // Valeur par défaut pour la hauteur
$min_width = 100; // Largeur minimale acceptable
$max_width = 800; // Largeur maximale acceptable
$min_height = 30; // Hauteur minimale acceptable
$max_height = 150; // Hauteur maximale acceptable
$width = isset($_GET['width']) ? max($min_width, min((int)$_GET['width'], $max_width)) : $default_width;
$height = isset($_GET['height']) ? max($min_height, min((int)$_GET['height'], $max_height)) : $default_height;
Lorsqu’on génère une image avec PHP, les dimensions de l’image doivent être des nombres entiers. Or, si les valeurs de largeur ($width
) ou de hauteur ($height
) sont calculées dynamiquement, elles peuvent parfois contenir des fractions (par exemple, 149.8 pixels). Cela peut poser des problèmes, car l’image générée pourrait être tronquée ou mal affichée.
Pour éviter cela, nous utilisons la fonction ceil()
qui arrondit chaque valeur à l’entier supérieur, garantissant ainsi que les dimensions de l’image sont bien valides.
// Arrondir les dimensions pour éviter des problèmes avec des fractions de pixels
$width = ceil($width);
$height = ceil($height);
Avant d’afficher une image, nous devons générer une opération mathématique simple. Par défaut, nous choisissons une addition ou une soustraction aléatoire, mais libre à vous d’adapter cette logique pour inclure d’autres types d’opérations, comme la multiplication ou la division. Ici, nous avons également décidé de limiter les valeurs des nombres entre 1 et 10 afin de garantir un défi accessible à l’utilisateur.
// Générer une opération mathématique simple
$num1 = rand(1, 10);
$num2 = rand(1, 10);
$operator = rand(0, 1) ? '+' : '-';
$captcha_code = $operator === '+' ? $num1 + $num2 : $num1 - $num2;
L’image qui sera générée devra afficher la représentation de cette opération algébrique sous la forme d’une question, par exemple : 5 + 3 = ?. Cette formule simple permet de créer une barrière efficace contre les robots tout en restant compréhensible et rapide à résoudre pour un humain.
Pour cela, nous devons d’abord préparer une image vierge sur laquelle sera inscrite cette opération. PHP dispose de la bibliothèque GD, qui permet de créer et manipuler des images dynamiquement.
Nous utilisons ici la fonction imagecreate()
, qui génère une image vide avec les dimensions définies précédemment ($width
et $height
).
// Créer une image avec les dimensions dynamiques
$image = imagecreate($width, $height);
if (!$image) {
throw new Exception("Erreur lors de la création de l'image.");
}
- Création de l’image vierge
imagecreate($width, $height)
: Génère une image en mémoire avec la largeur et la hauteur spécifiées.- La fonction retourne une ressource d’image qui sera ensuite manipulée pour y ajouter du texte et du bruit visuel.
- Gestion des erreurs
- Si l’image ne peut pas être créée (manque de mémoire, erreur serveur…), la fonction retourne
false
. - Dans ce cas, nous lançons une exception avec
throw new Exception()
, ce qui permet de stopper immédiatement l’exécution du script et d’éviter un affichage corrompu.
- Si l’image ne peut pas être créée (manque de mémoire, erreur serveur…), la fonction retourne
Maintenant que nous avons préparé un support graphique vierge, nous devons définir les couleurs qui seront utilisées pour le fond et le texte de l’image. La bibliothèque GD permet d’allouer des couleurs grâce à la fonction imagecolorallocate()
.
// Couleurs
$background = imagecolorallocate($image, 255, 255, 255); // Blanc
$text_color = imagecolorallocate($image, 0, 0, 0); // Noir
- Définition du fond de l’image
imagecolorallocate($image, 255, 255, 255)
: Définit une couleur blanche (RGB : 255, 255, 255
) pour le fond de l’image.- En appelant cette fonction en premier, la couleur est automatiquement appliquée à tout l’arrière-plan de l’image.
- Définition de la couleur du texte
imagecolorallocate($image, 0, 0, 0)
: Définit une couleur noire (RGB : 0, 0, 0
) qui sera utilisée pour afficher l’opération mathématique.
Ces deux couleurs permettent d’assurer une bonne lisibilité du CAPTCHA, avec un texte sombre sur un fond clair. Toutefois, rien ne vous empêche de personnaliser cette palette de couleurs en fonction du style souhaité pour le CAPTCHA.
À présent, nous allons inscrire l’opération mathématique sur l’image. Pour cela, nous définissons la chaîne de caractères qui représentera le CAPTCHA, ainsi que la taille du texte à afficher.
// Texte à afficher
$captcha_text = "{$num1} {$operator} {$num2} = ?";
$font_size = 5;
- Construction de la chaîne de caractères
- La variable
$captcha_text
assemble les valeurs générées précédemment ($num1
,$operator
,$num2
) sous la forme d’une question, par exemple : « 5 + 3 = ? ». - Cette notation simple et directe garantit une lecture facile pour l’utilisateur.
- La variable
- Définition de la taille du texte
$font_size = 5;
définit la taille du texte. La fonctionimagestring()
, que nous utiliserons pour afficher le texte, supporte des tailles allant de 1 (très petit) à 5 (plus grand).- Il est possible d’utiliser une police personnalisée avec
imagettftext()
, ce qui permettrait d’appliquer différentes typographies et tailles de police pour complexifier l’identification par des robots.
Aller plus loin : Complexifier l’affichage du texte
Si vous souhaitez rendre le CAPTCHA plus difficile à lire pour les robots, vous pouvez :
- Alterner les tailles de police pour chaque caractère.
- Utiliser une police aléatoire parmi une sélection prédéfinie.
- Appliquer une légère rotation sur chaque chiffre ou opérateur pour perturber la reconnaissance automatique.
Plaçons notre équation au sein de l’image
Nous allons maintenant positionner le texte au bon endroit sur l’image, afin qu’il soit bien centré et lisible. Pour que le texte soit bien visible et correctement positionné sur l’image, nous devons le centrer en fonction de la largeur et de la hauteur définies précédemment.
// Position du texte (centré)
$text_width = imagefontwidth($font_size) * strlen($captcha_text);
$text_height = imagefontheight($font_size);
$x = ($width - $text_width) / 2;
$y = ($height - $text_height) / 2;
imagestring($image, $font_size, $x, $y, $captcha_text, $text_color);
La première étape consiste à calculer la largeur et la hauteur du texte :
imagefontwidth($font_size) * strlen($captcha_text)
:- Détermine la largeur totale du texte en multipliant la largeur d’un caractère par le nombre total de caractères dans la chaîne
$captcha_text
.
- Détermine la largeur totale du texte en multipliant la largeur d’un caractère par le nombre total de caractères dans la chaîne
imagefontheight($font_size)
:- Récupère la hauteur d’un caractère en fonction de la taille définie (
$font_size
).
- Récupère la hauteur d’un caractère en fonction de la taille définie (
Ensuite, nous calculons les coordonnées (x, y)
pour centrer le texte :
($width - $text_width) / 2
:- Positionne le texte horizontalement en soustrayant sa largeur totale à celle de l’image, puis en divisant par 2 pour obtenir un centrage parfait.
($height - $text_height) / 2
:- Même logique pour la position verticale, afin que l’opération mathématique soit bien placée au milieu de l’image.
Enfin, nous dessinons le texte sur l’image avec imagestring()
:
$image
: L’image sur laquelle le texte sera inscrit.$font_size
: Taille de la police utilisée.$x, $y
: Coordonnées du point où le texte doit être affiché.$captcha_text
: Le texte représentant l’opération mathématique.$text_color
: La couleur du texte définie précédemment.
Ce procédé garantit un affichage équilibré, évitant que l’opération ne soit trop décalée vers un bord ou mal positionnée. Si l’on voulait pousser plus loin la personnalisation, nous pourrions utiliser une police TrueType (TTF) avec imagettftext()
pour plus de flexibilité sur le style d’écriture.
Ajout de bruit pour complexifier la lecture
Un CAPTCHA efficace ne doit pas être trop facile à lire pour un robot, tout en restant compréhensible pour un humain. L’objectif est donc d’ajouter des éléments de perturbation qui compliquent l’identification du texte par des algorithmes de reconnaissance optique (OCR).
Dans cette section, nous allons intégrer plusieurs méthodes de brouillage :
- Des lignes aléatoires, qui traversent l’image pour masquer partiellement le texte.
- Des arcs courbes, qui ajoutent du bruit visuel tout en conservant une apparence aléatoire.
- Des nuages de points, qui accentuent l’effet de désordre pour perturber les algorithmes d’analyse d’image.
Nous pourrions aller encore plus loin en insérant des images parasites, des effets de distorsion ou des textures aléatoires pour rendre le CAPTCHA encore plus difficile à automatiser.
Ajout de lignes aléatoires
Les lignes aléatoires sont un moyen simple et efficace de cacher partiellement les caractères du CAPTCHA. Elles ne doivent pas complètement masquer le texte, mais suffisent à perturber un algorithme de reconnaissance.
// Ajouter des lignes de bruit
$line_color = imagecolorallocate($image, 200, 200, 200); // Gris clair
for ($i = 0; $i < 12; $i++) {
imageline($image, rand(0, $width), rand(0, $height), rand(0, $width), rand(0, $height), $line_color);
}
- On définit une couleur gris clair (
200, 200, 200
) pour éviter qu’elle ne prenne trop d’importance par rapport au texte. - Une boucle crée 12 lignes aléatoires traversant l’image.
- La fonction
imageline()
dessine une ligne entre deux points choisis aléatoirement (rand(0, $width)
,rand(0, $height)
).
Ces lignes simulent des interférences visuelles, mais restent suffisamment légères pour ne pas gêner un humain.
Ajout d’arcs courbes
Les arcs sont un excellent moyen d’ajouter du bruit tout en conservant une forme naturelle et irrégulière, plus difficile à traiter pour un OCR.
// Ajouter des arcs de bruit
for ($i = 0; $i < 5; $i++) {
imagearc(
$image,
rand(0, $width),
rand(0, $height),
rand(20, 100),
rand(10, 50),
rand(0, 360),
rand(0, 360),
$line_color
);
}
- Une boucle crée 5 arcs aléatoires sur l’image.
- La fonction
imagearc()
dessine un arc elliptique entre deux angles (définis en degrés). - Chaque arc est placé aléatoirement et varie en taille et en inclinaison pour ajouter du bruit visuel.
L’effet obtenu est un désordre fluide et naturel, qui perturbe la reconnaissance des caractères sans masquer complètement le texte.
Ajout d’un nuage de points aléatoires
Les points dispersés aléatoirement créent un effet granuleux, qui brouille encore plus la lisibilité pour un robot.
// Ajouter des points aléatoires
for ($i = 0; $i < 1000; $i++) {
imagesetpixel($image, rand(0, $width), rand(0, $height), $dot_color);
}
- Une boucle génère 1000 points aléatoires répartis sur l’image.
- La fonction
imagesetpixel()
place un pixel coloré à une position aléatoire. - La couleur des points peut être différente de celle des lignes et des arcs, créant un effet de bruit visuel supplémentaire.
Les points aléatoires sont une technique de perturbation efficace, car ils remplissent l’espace sans structuration prévisible.
Aller encore plus loin dans le brouillage

Avec ces trois méthodes, nous avons déjà grandement détérioré la lisibilité automatique du CAPTCHA. Mais il est possible d’augmenter encore la complexité en :
- Appliquant une déformation du texte (rotation, inclinaison, effet d’ondulation).
- Superposant des images parasites ou des motifs en arrière-plan.
- Ajoutant un effet de flou ou de texture pour imiter du papier froissé.
- Utilisant des polices variées et irrégulières pour chaque caractère.
L’objectif final est de rendre l’extraction du texte par un robot aussi difficile que possible, tout en gardant un CAPTCHA lisible et rapide à résoudre pour un humain.
Gestion du token et sécurisation
Lors de l’exécution du script captcha_math.php
, il est primordial de gérer le stockage et la validation du token pour s’assurer qu’un CAPTCHA ne puisse être réutilisé ou manipulé frauduleusement. Le token permet d’identifier chaque défi de manière unique et temporaire. Nous devons donc :
- Stocker le token et le code du CAPTCHA dans un fichier accessible côté serveur.
- Nettoyer les tokens expirés afin d’éviter une accumulation inutile et d’optimiser la sécurité.
- Générer un nouveau token unique pour chaque CAPTCHA affiché.
- Enregistrer et sauvegarder le token généré avec sa réponse associée.
- Protéger l’accès au script contre les requêtes non autorisées
Ces étapes garantissent que chaque CAPTCHA est bien lié à une interaction utilisateur réelle et ne peut être détourné par des scripts automatisés. Voyons comment ces étapes sont mises en place dans le script.
Pour assurer le suivi et la validation des CAPTCHA, nous devons stocker temporairement chaque token généré ainsi que la réponse correcte associée. Plutôt que d’utiliser une base de données, nous optons ici pour un fichier JSON (captcha_tokens.json
), qui permet une gestion simple et efficace des données.
Ce fichier contiendra une liste de tokens, où chaque entrée correspond à un CAPTCHA généré et conserve les informations suivantes :
- Le token unique généré pour identifier un CAPTCHA.
- La réponse correcte que l’utilisateur devra fournir.
- Un horodatage pour suivre l’ancienneté du token et gérer son expiration.
Nous devons donc, à chaque exécution du script, ouvrir ce fichier et récupérer les tokens existants, ou en créer un nouveau s’il n’existe pas encore. Voici le code permettant de charger et initialiser le fichier JSON :
// Chargement du fichier des tokens
$file = __DIR__ . '/captcha_tokens.json'; // Fichier JSON pour stocker les tokens
$tokens = file_exists($file) ? json_decode(file_get_contents($file), true) : [];
- Définition du fichier JSON
__DIR__ . '/captcha_tokens.json'
:__DIR__
représente le répertoire courant du script.- Nous construisons ainsi le chemin absolu du fichier pour éviter toute erreur liée à un appel depuis un autre dossier.
- Vérification de l’existence du fichier
file_exists($file) ? ... : [];
- Si le fichier existe, nous le lisons et décodons son contenu en un tableau PHP grâce à
json_decode()
. - Si le fichier n’existe pas, nous initialisons
$tokens
avec un tableau vide[]
.
Pourquoi stocker les tokens dans un fichier JSON ?
- Simplicité : Pas besoin d’une base de données pour une fonctionnalité aussi légère.
- Performance : La lecture/écriture est rapide tant que le nombre de tokens reste raisonnable.
- Flexibilité : Le fichier peut être facilement lu, modifié et sauvegardé sous forme de structure JSON.
Cette étape nous permet donc de charger la liste des tokens en mémoire avant d’y ajouter de nouveaux éléments ou d’effectuer un nettoyage. Nous allons maintenant voir comment supprimer les tokens expirés pour éviter toute surcharge inutile.
Ce code s’inscrit dans la continuité de la gestion des tokens en assurant leur nettoyage automatique. Une fois les tokens chargés depuis le fichier JSON, nous devons nous assurer qu’ils ne s’accumulent pas inutilement. Un CAPTCHA doit être temporaire et ne doit pas pouvoir être réutilisé après un certain délai.
// Nettoyer les tokens expirés
$expiration_time = 3600; // 1 heure
$current_time = time();
$tokens = array_filter($tokens, function ($timestamp) use ($current_time, $expiration_time) {
return ($current_time - $timestamp['created_at']) < $expiration_time;
});
- Définition de la durée de vie d’un token
$expiration_time = 3600;
: Définit une expiration de 1 heure (3600 secondes).- Un token plus ancien que cette limite sera supprimé pour éviter toute réutilisation.
- Récupération du timestamp actuel
$current_time = time();
permet de comparer l’âge de chaque token avec le timestamp actuel.
- Filtrage des tokens valides
array_filter($tokens, function ($timestamp) { ... })
:- Parcourt le tableau
$tokens
et ne conserve que ceux dont l’horodatage (created_at
) est inférieur à l’expiration définie. - Les tokens trop anciens sont automatiquement supprimés.
- Parcourt le tableau
Pourquoi ce nettoyage est essentiel ?
- Éviter une accumulation inutile : À long terme, sans suppression, le fichier JSON pourrait devenir volumineux et ralentir la lecture/écriture.
- Renforcer la sécurité : Un ancien CAPTCHA ne doit pas être soumis à nouveau après une période prolongée.
- Optimiser les performances : Seuls les CAPTCHA actifs restent enregistrés, garantissant une meilleure gestion des ressources serveur.
Avec cette étape, nous avons désormais une liste de tokens mise à jour en temps réel, ne contenant que des entrées valables. Nous allons maintenant voir comment générer un nouveau token unique et l’ajouter à ce fichier.
Après avoir nettoyé les anciens tokens, nous devons maintenant générer un nouveau token unique et l’enregistrer dans notre fichier JSON avec la réponse correcte du CAPTCHA. Ce token servira d’identifiant pour associer chaque défi à une réponse attendue.
// Générer un token unique
$token = bin2hex(random_bytes(16));
// Enregistrer le nouveau token avec la date de création
$tokens[$token] = [
'code' => $captcha_code,
'created_at' => time() // Timestamp actuel
];
// Sauvegarde dans le fichier JSON
file_put_contents($file, json_encode($tokens, JSON_PRETTY_PRINT));
- Génération d’un identifiant unique
random_bytes(16)
: Génère 16 octets de données aléatoires, garantissant une forte entropie et évitant toute prédiction possible.bin2hex(...)
: Convertit ces octets en une chaîne hexadécimale de 32 caractères, parfaite pour être utilisée comme clé unique.
214fdfa207f6f21216f26a8b5f6b2f2a
- Stockage du token avec les données associées
- Le token sert de clé dans le tableau
$tokens
et contient :code
: La réponse correcte au défi mathématique.created_at
: L’horodatage de création, utile pour la gestion des expirations.
- Le token sert de clé dans le tableau
- Sauvegarde du fichier mis à jour
file_put_contents($file, json_encode($tokens, JSON_PRETTY_PRINT));
- Convertit le tableau
$tokens
en format JSON puis l’enregistre dans le fichier. - L’option
JSON_PRETTY_PRINT
est ajoutée pour rendre le fichier plus lisible lors du débogage.
Pourquoi cette approche est sécurisée ?
- Unicité garantie : Chaque CAPTCHA a un identifiant unique et aléatoire.
- Difficulté de prédiction :
random_bytes()
est cryptographiquement sécurisé. - Empêche la réutilisation : Les anciens tokens sont nettoyés avant chaque génération.
Avec cette étape, nous avons maintenant un nouveau CAPTCHA enregistré, prêt à être envoyé au client et à être validé lorsqu’un utilisateur soumettra le formulaire. Avant cela, nous devons encore protéger l’accès au script pour éviter les requêtes malveillantes.
Protéger l’accès au script contre les requêtes non autorisées
À ce stade, notre système génère un CAPTCHA et stocke ses informations côté serveur. Cependant, nous devons nous assurer que seules les pages légitimes peuvent accéder à notre script captcha_math.php
.
Sans protection, un attaquant pourrait :
- Générer des CAPTCHA à la demande pour analyser leur structure.
- Effectuer des requêtes massives pour perturber le fonctionnement du serveur.
- Exploiter le système pour d’autres usages non prévus.
Une méthode simple pour limiter cela consiste à vérifier l’origine de la requête grâce à l’en-tête HTTP_REFERER
:
// Vérifier que la requête provient de votre site
$allowed_referer = "https://votreserveur.com"; // Modifier selon votre domaine
if (!isset($_SERVER['HTTP_REFERER']) || strpos($_SERVER['HTTP_REFERER'], $allowed_referer) !== 0) {
http_response_code(403); // Accès interdit
exit('Accès non autorisé.');
}
- Définition du domaine autorisé
$allowed_referer
contient l’URL de votre site (remplacez"https://votreserveur.com"
par votre domaine réel).
- Vérification de l’origine de la requête
$_SERVER['HTTP_REFERER']
indique l’URL de la page qui a envoyé la requête.strpos($_SERVER['HTTP_REFERER'], $allowed_referer) !== 0
s’assure que l’URL commence bien par notre domaine.
- Blocage des requêtes non autorisées
- Si le
Referer
est absent ou ne correspond pas au site autorisé, nous retournons un code HTTP 403 (Accès interdit) et stoppons immédiatement le script avecexit()
.
- Si le
Limitations et alternatives
Bien que cette méthode soit simple, HTTP_REFERER
peut être falsifié par un attaquant expérimenté. Pour renforcer la sécurité, vous pouvez :
- Utiliser un système de clé API qui valide l’accès avec un jeton sécurisé.
- Exiger une authentification utilisateur pour restreindre l’usage aux visiteurs connectés.
- Limiter les requêtes par IP ou session pour éviter les abus.
Avec cette protection en place, nous réduisons les risques d’utilisation abusive de notre script et garantissons que seules les pages autorisées peuvent interagir avec notre CAPTCHA.
Protection supplémentaire avec .htaccess
En complément de la vérification de l’origine dans captcha_math.php
, nous pouvons restreindre directement l’accès au script via un fichier .htaccess
. Cela empêche les accès directs non autorisés avant même l’exécution du script PHP.
<Files "captcha_math.php">
Require all denied
Require host votreserveur.com
</Files>
Ce fichier bloque toutes les requêtes, sauf celles provenant du domaine autorisé. Pour une explication détaillée des règles .htaccess
et d’autres méthodes de sécurisation, consultez notre article dédié : Optimiser la sécurité d’un script PHP avec .htaccess
Renvoi des données à l’appel AJAX
Une fois l’image du CAPTCHA générée et le token enregistré, nous devons retourner ces données au navigateur sous une forme exploitable par notre script JavaScript. L’image sera envoyée en base64 et le token permettra d’identifier la réponse attendue lors de la validation du formulaire.
// Spécifier le format de la réponse
header('Content-Type: application/json');
// Capturer l'image en mémoire et encoder en base64
ob_start();
imagepng($image);
$image_data = base64_encode(ob_get_clean());
// Libérer la mémoire
imagedestroy($image);
// Retourner l'image et le token au format JSON
echo json_encode([
'image' => "data:image/png;base64,$image_data",
'token' => $token
]);
- Définition du type de réponse
header('Content-Type: application/json');
: Indique que le serveur envoie un fichier JSON, permettant au script JavaScript de l’interpréter facilement.
- Conversion de l’image en base64
ob_start();
: Active un tampon de sortie pour capturer l’image en mémoire.imagepng($image);
: Génère l’image PNG.base64_encode(ob_get_clean());
: Récupère le contenu du tampon et le convertit en base64, ce qui permet d’intégrer l’image directement dans un élément<img>
sans fichier externe.
- Libération de la mémoire
imagedestroy($image);
: Supprime l’image de la mémoire du serveur pour éviter les fuites.
- Encapsulation des données en JSON
json_encode([...]);
retourne un objet contenant :image
: L’image en base64 prête à être affichée dans une balise<img>
.token
: Le token généré, qui sera envoyé avec le formulaire pour la validation.
Ce format permet un échange fluide et sécurisé entre le serveur et le navigateur. Lorsqu’une nouvelle image est demandée (ex : rechargement du CAPTCHA), un nouveau token est généré, garantissant qu’une seule réponse est valable à la fois.
Validation des données transmises par le formulaire validate_captcha.php
Jusqu’à présent, nous avons vu comment générer un CAPTCHA et associer un token unique côté serveur, puis envoyer ces informations au navigateur. Nous allons maintenant nous concentrer sur l’étape inverse : la transmission des données depuis le formulaire vers le serveur, et plus précisément vers le fichier validate_captcha.php
. Cette validation est essentielle pour nous assurer que la réponse soumise correspond bien à un défi valide et qu’aucun CAPTCHA expiré ou erroné ne puisse être utilisé.
Avant de traiter la validation, nous devons nous assurer que les champs requis sont bien renseignés et que les données transmises
// Récupérer et vérifier le CAPTCHA
$captchaToken = $_POST['captcha_token'] ?? null;
$captchaInput = trim($_POST['captcha_input'] ?? '');
// Vérifier si le token et la réponse sont fournis
if (!$captchaToken || $captchaInput === '') {
die('Erreur: CAPTCHA manquant.');
}
Avant de pouvoir valider la réponse de l’utilisateur, nous devons accéder aux données stockées dans captcha_tokens.json
. Ce fichier contient les tokens générés précédemment ainsi que leur réponse attendue. Nous allons donc charger son contenu et le reconvertir à la volée en un tableau PHP exploitable.
// Charger les tokens et leurs valeurs depuis le stockage
$file = __DIR__ . '/captcha_tokens.json';
$tokens = file_exists($file) ? json_decode(file_get_contents($file), true) : [];
Faut-il nettoyer les tokens expirés ?
Dans notre implémentation actuelle, le nettoyage des tokens expirés est effectué lors de la génération d’un nouveau CAPTCHA. Cela signifie qu’en principe, à ce stade, seuls les tokens valides devraient être présents dans le fichier
captcha_tokens.json
.Cependant, si le formulaire est utilisé de manière intensive avec un grand nombre de requêtes simultanées, il peut être pertinent d’exécuter un nettoyage supplémentaire ici. Une autre approche serait de mettre en place une tâche en arrière-plan (cron job) qui s’occuperait du nettoyage des tokens obsolètes à intervalles réguliers, évitant ainsi une surcharge inutile du serveur lors de chaque validation.
Avant de procéder à la validation de la réponse, nous devons nous assurer que le token soumis par l’utilisateur est bien présent dans notre fichier de stockage. Chaque CAPTCHA généré possède un token unique qui lui est associé, et ce token doit exister dans notre fichier captcha_tokens.json
pour être considéré comme valide.
// Vérifier si le token soumis existe bien dans notre fichier de stockage
if (!$captchaToken || !isset($tokens[$captchaToken])) {
die('Erreur: CAPTCHA invalide ou expiré.');
}
Une fois que nous avons confirmé l’existence du token, nous devons maintenant comparer la réponse soumise par l’utilisateur avec la réponse attendue.
// Vérifier si la réponse saisie correspond bien au code stocké
if ((string)$tokens[$captchaToken]['code'] !== (string)$captchaInput) {
die('Erreur: CAPTCHA incorrect.');
}
Si toutes les vérifications sont correctes, nous pouvons alors supprimer le token du fichier de stockage, afin qu’il ne puisse pas être réutilisé.
// Supprimer le token après validation
unset($tokens[$captchaToken]);
file_put_contents($file, json_encode($tokens, JSON_PRETTY_PRINT));
Cette approche garantit que chaque CAPTCHA est unique et ne peut être utilisé qu’une seule fois. Une fois le formulaire validé, le token est immédiatement supprimé, empêchant toute tentative de réutilisation.
Avec cette dernière étape, notre système de CAPTCHA est désormais complet : nous avons un mécanisme robuste pour générer, afficher, sécuriser et valider un CAPTCHA mathématique en PHP.
Conclusion
Nous avons maintenant mis en place la génération et la gestion sécurisée du CAPTCHA côté serveur. Grâce à nos scripts captcha_math.php
et validate_captcha.php
, nous avons réussi à :
- Créer une image dynamique contenant une opération mathématique simple.
- Gérer les tokens pour lier chaque CAPTCHA à une réponse unique et temporaire.
- Envoyer les données au client sous forme de JSON, facilitant leur intégration dans l’interface utilisateur.
- Vérifier la réponse fournie par l’utilisateur lors de la soumission du formulaire.
Ce CAPTCHA est désormais pleinement fonctionnel et permet une validation robuste des formulaires. Toutefois, selon les besoins spécifiques de votre projet, plusieurs améliorations peuvent être envisagées :
- Diversifier les défis : Ajouter des opérations plus complexes ou un système basé sur des images pour renforcer la sécurité.
- Optimiser la gestion des tokens : Passer du stockage en fichier JSON à une base de données si le site gère un grand nombre d’utilisateurs.
- Modulariser l’intégration : Créer une classe PHP dédiée pour rendre ce CAPTCHA facilement réutilisable dans différents formulaires.
Ces pistes d’amélioration vous permettront d’adapter cette solution à des contextes variés, en fonction des besoins en termes de sécurité et d’expérience utilisateur. : nous avons un mécanisme robuste pour générer, afficher, sécuriser et valider un CAPTCHA mathématique en PHP.