37

Introduction

Adieu spam !! Eh oui ce tutorial va vous permettre de créer votre propre captcha anti-spam. Vous pouvez sans tarder le voir en action sur la page de démo.
Tout d’abord qu’est-ce qu’un captcha? C’est un test utilisé pour différencier les humains des machines. Ici en l’occurence, il s’agit d’une image générée dynamiquement avec PHP et qui contient du texte, qu’il faut tout simplement lire et recopier. Simple pour un humain mais difficile à interpréter pour une machine. Cela constitue donc une première barrière pour empêcher le spam dans vos formulaires web.
Bien sûr, les captchas visuels ne sont pas infaillibles; en effet, certains logiciels (OCR) permettent de reconnaitre des caractères, même sur une image ! C’est pourquoi nous essaierons de brouiller un peu les pistes en ajoutant un dégradé de couleurs en fond, et en utilisant plusieurs polices de caractères, plusieurs couleurs, et différentes dispositions des caractères, en faisant attention à ce que ça reste lisible pour l’humain.

Avant de commencer

Pour manipuler des images de façon dynamique avec PHP, il faut que la bibliothèque GD2 soit installée et activée sur votre hébergeur. C’est le cas sur la plupart des hébergements payants, et même sans doute gratuits.
Nous utiliserons une image captcha.png (arrière-plan de la future image), un fichier captcha.php (image à afficher), un fichier index.php (formulaire) et des polices de caractères TrueType (autant que vous voulez) à placer dans le dossier fonts.
Si vous ne modifiez pas les chemins du script, l’arborescence des fichiers doit donc s’organiser comme suit :

/dossier/image.png
/dossier/captcha.php
/dossier/index.php
/dossier/fonts/font-a.ttf
/dossier/fonts/font-b.ttf

 

Fonctionnement

La création de l’image

On part d’une image captcha.png vierge de tout texte.

Arrière-plan Captcha

Le script captcha.php va récupérer cette image pour écrire dessus dynamiquement. Pour cela, il va d’abord générer un code aléatoire de cinq chiffres et lettres. Il va ensuite chercher les polices TrueType stockées dans le dossier fonts et les appliquer au hasard à chacun des caractères de la chaîne générée, en n’oubliant pas de les personnaliser un peu (taille, inclinaison, disposition, couleur).
Une fois le traitement terminé, à chaque appel du script, l’image sera envoyée au navigateur. L’affichage ne se fait alors plus par une image en dur, mais par captcha.php qui devient une image réelle au yeux du navigateur.

Captcha Anti-Spam

La vérification

En haut des fichiers captcha.php et index.php remarquez qu’on démarre à chaque fois une session avec session_start(). Dans captcha, cette session nous sert de stockage, alors que dans index.php, on l’utilise pour récupérer ce qui a été stocké.
Lorsque la chaîne aléatoire est générée par captcha.php, celle-ci est cryptée avec md5() puis stockée dans une variable de session $_SESSION['captcha'].
Après l’envoi du formulaire par l’utilisateur, le code qu’il a entré est récupéré, crypté par le même algorithme md5() pour être finalement comparé à la variable de session précédemment créée. Si le code crypté correspond à la chaîne cryptée, tout va bien, sinon l’utilisateur peut recommencer.

Mise en place

L’image dynamique captcha.php

<?php
/* ////////////////////
	INITIALISATION
//////////////////// */

/* Démarrage d'une session qui va nous permettre de stocker la valeur à recopier. */
session_start(); // session_start() se place toujours avant toute sortie vers la page web

/* Chemin absolu vers le dossier */
if ( !defined('ABSPATH') ) define('ABSPATH', dirname(__FILE__) . '/');

/*
Création d'une fonction pour générer la chaîne aléatoire à recopier (sans cryptage) :
- strlen() retourne la taille de la chaine en paramètre
- mt_rand(a, b) génère un nombre aléatoire entre a et b compris : cette fonction est plus rapide que rand() de la bibliothèque standard
- $chars{0} retourne le premier caractère de la chaîne $chars, $chars{1} le deuxième ...
*/
function getCode($length) {
	$chars = '23456789ABCDEFGHJKLMNPQRSTUVWXYZ'; // Certains caractères ont été enlevés car ils prêtent à confusion
	$rand_str = '';
	for ($i=0; $i<$length; $i++) {
		$rand_str .= $chars{ mt_rand( 0, strlen($chars)-1 ) };
	}
	return $rand_str;
}

/* Stockage de la chaîne aléatoire de 5 caractères obtenue */
$theCode = getCode(5);

/* Cryptage de la chaine avec md5() avant de la stocker dans la variable de session $_SESSION['captcha'] de la session en cours.
C'est à cette variable qu'on va comparer le code entré par l'utilsateur dans le formulaire. */
$_SESSION['captcha'] = md5($theCode);

/* Afin de traiter les caractères séparément, on les stocke un par un dans des variables. */
$char1 = substr($theCode,0,1);
$char2 = substr($theCode,1,1);
$char3 = substr($theCode,2,1);
$char4 = substr($theCode,3,1);
$char5 = substr($theCode,4,1);

/*
glob() retourne un tableau répertoriant les fichiers du dossier 'fonts', ayant l'extension .ttf ( pas .TTF ! ).
Vous pouvez donc ajouter autant de polices TrueType que vous désirez, en veillant à les renommer.
*/
$fonts = glob('fonts/*.ttf');

/* //////////////////////////////
	TRAITEMENT DE L'IMAGE
////////////////////////////// */

/*
imagecreatefrompng() crée une nouvelle image à partir d'un fichier PNG.
Cette nouvelle $image va être ensuite modifiée avant l'affichage.
 */
$image = imagecreatefrompng('captcha.png');

/*
imagecolorallocate() retourne un identifiant de couleur.
On définit les couleurs RVB qu'on va utiliser pour nos polices et on les stocke dans le tableau $colors[].
Vous pouvez ajouter autant de couleurs que vous voulez.
*/
$colors=array (	imagecolorallocate($image, 131,154,255),
				imagecolorallocate($image, 89,186,255),
				imagecolorallocate($image, 155,190,214),
				imagecolorallocate($image, 255,128,234),
				imagecolorallocate($image, 255,123,123) );

/* Création d'une petite fonction qui retourne une VALEUR aléatoire du tableau reçu en paramètre. */
function random($tab) {
	return $tab[array_rand($tab)];
}

/*
Mise en forme de chacun des caractères et placement sur l'image.
imagettftext(image, taille police, inclinaison, coordonnée X, coordonnée Y, couleur, police, texte) écrit le texte sur l'image.
*/
imagettftext($image, 28, -10, 0, 37, random($colors), ABSPATH .'/'. random($fonts), $char1);
imagettftext($image, 28, 20, 37, 37, random($colors), ABSPATH .'/'. random($fonts), $char2);
imagettftext($image, 28, -35, 55, 37, random($colors), ABSPATH .'/'. random($fonts), $char3);
imagettftext($image, 28, 25, 100, 37, random($colors), ABSPATH .'/'. random($fonts), $char4);
imagettftext($image, 28, -15, 120, 37, random($colors), ABSPATH .'/'. random($fonts), $char5);

/* //////////////////////////////
	FIN => ENVOI DE L'IMAGE
////////////////////////////// */

/*
Comme c'est le fichier image.php et non captcha.png qui va être appelé,
on envoie un en-tête HTTP au navigateur via header() pour lui indiquer
que image.php est bien une image au format PNG.
*/
header('Content-Type: image/png');

/* .. et on envoie notre image PNG au navigateur. */
imagepng($image);

/* L'image ayant été envoyée, on libère toute la mémoire qui lui est associée via imagedestroy(). */
imagedestroy($image);
?>

Le formulaire index.php

Ce fichier contient le formulaire de vérification et c’est donc lui qui va afficher l’image. C’est également au sein de ce fichier qu’on traite les données reçues une fois le formulaire envoyé. Il va comparer le code entré par l’utilisateur, à la chaîne stockée dans la variable de session précédemment créée, tout ça en cryptage md5().
N’oubliez pas de mettre le session_start() en début de fichier !

<?php
/* IMPORTANT : Redémarrage de la session */
session_start();
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html dir="ltr" xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head>
<title>Captcha Anti-Spam</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
</head>

<body>
<div>

	<?php
	/* Si l'utilisateur a bien entré un code */
	if (!empty($_REQUEST['userCode']))
	{
		/* Conversion en majuscules */
		$userCode = strtoupper($_REQUEST['userCode']);

		/* Cryptage et comparaison avec la valeur stockée dans $_SESSION['captcha'] */
		if( md5($userCode) == $_SESSION['captcha'] )
			echo '<h2 class="correct">Correct !</h2>'; // Le code est bon
		else echo '<h2 class="incorrect">Ahem.. Recommencez !</h2>'; // Le code est erroné
	}
	/* Si aucun code n'a été entré */
	else echo '<h2>Etes-vous un bot ?</h2>';
	?>

	<!-- Formulaire $_SERVER['PHP_SELF'] signifie que le traitement du formulaire se fait sur la même page -->
	<form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>">
	<div>
		<!-- Notre image créée -->
		<img src="captcha.php" alt="Captcha" id="captcha" />

		<!-- (JavaScript) Changer d'image à la volée si elle est illisible  -->
		<a style="cursor:pointer" onclick="document.images.captcha.src='captcha.php?id='+Math.round(Math.random(0)*1000)+1">Recharger l'image</a>

		<p>
			<label for="userCode">Entrez le code<br />insensible à la cAsSe</label>
			<input name="userCode" id="userCode" type="text" />
			<input type="submit" name="submit" id="submit" value="" />
		</p>
	</div>
	</form>

</div>
</body>

Conclusion

Enfin, sachez aussi qu’il existe également des captchas audios, mais aussi des captchas textuels qui se basent sur la réflexion de l’utilisateur et non sur la simple lecture : par exemple, combien font 1+1?, ainsi que d’autres petites astuces pour freiner le spam, que je traiterai peut-être dans un autre tutorial :)

37 commentaires

1. Bouctoubou :

Intéressant, je connaissais pas ton blog, merci pour la découverte!

04.09.08 - 11:37

2. Laurent :

Intéressant cet article, cependant le fait d’utiliser des couleurs peu perturber les personnes souffrant de déficience visuelle (les daltoniens par exemple) !

07.09.08 - 20:32

3. digg-design.fr - le blog! » Archive du blog » Articles les plus votés :

[...] PHP : création d’un captcha anti-spam [...]

24.09.08 - 10:39

4. jo :

bonjour ,

Je cherche un captcha pour une page sur un serveur de FREE.FR

——————
… des captchas textuels qui se basent sur la réflexion de l’utilisateur et non sur la simple lecture : par exemple, combien font 1+1?, …

Les sessions ne passent pas sur un serveur FREE , alors est ce possible de trouver un captcha ( sans session )

Aplus
@+

27.09.08 - 12:07

5. NotoOn :

Bonjour,

chez FREE le répertoire de sessions est placé à la racine de votre site. Il faut donc créer un répertoire ’sessions’ à la racine de votre site pour que cela fonctionne.

27.09.08 - 17:26

6. jo :

#
/* Chemin absolu vers le dossier */
#
if ( !defined(’ABSPATH’) ) define(’ABSPATH’, dirname(__FILE__) . ‘/’);
#
————————-

Mon dossier est dans /form/ , alors comment modifier la ligne du dessus !!!
:)

01.10.08 - 19:38

7. kraddle :

plus simple :
mettre en place un champ invisible pour l’internaute mais pas pour le robot qui lui va remplir ce champs( hihihihi)

il suffit dans un traitement en PHP de vérifier l’existence ou non de cette variable :

11.10.08 - 21:03

8. kraddle :

arf j’avais mis du code HTML pour l’explication mais cela apparait pas
j’esxplique donc :donc ce n’est pas un champs hidden mais caché avec la propriété CSS display:none;

voilà désolé pour le flood

argl !!

11.10.08 - 21:06

9. Patricia :

Bonjour,

Tout d’abord merci pour ce tutoriel. Il est clair et facile à comprendre même pour une « presque»  débutante comme moi.

Il y a juste une chose que je ne m’explique pas : j’ai testé ce captcha tel que vous le proposez, afin de voir si je n’ai pas fait d’erreur d’installation : tout est OK.
J’y intègre un questionnaire classique de contact et là, l’image (les lettres) du captcha ne se fait pas à l’ouverture de la page, et je n’ai qu’un rectangle vide… il faut recharger l’image pour qu’elle apparaisse. Les fonctions d’envoi et d’erreur fonctionnant correctement.

Je précise que j’ai bien laissé la ligne concernant la session en début de page.

24.10.08 - 19:09

10. 3db :

Salut, trés bon tuto merci
juste un truc qui dérange un peu la zone du script qui est génante a lire et a cipier

encore merci ;)

11.01.09 - 20:00

11. Patrick :

Bonjour,
J’ai suivi pas à pas la procédure mais l’image ne s’affiche pas :(
Je suis sur un serveur IIS, GD est installé (GD Version : bundled (2.0.15 compatible) )
J’ai bien le dossier fonts, avec les fontes .ttf (en minuscules), le fichier index.php (recopié de ci-dessus), le fichier captcha.php (recopié de ci-dessus) et l’image captcha.png.
Une suggestion?
Merci

07.04.09 - 16:24

12. Ala :

bonjour à tous,
@Patrick : tu dois l’exécuter en ligne, ça marche pas en local.

13.04.09 - 18:12

13. Eraser :

Un grand merci et un grand bravo :)

15.04.09 - 11:47

14. laurent :

bonjour,

J’ai mis les données du index.php sous mon formulaire, respecté l’arborescence des dossiers, le captcha laisse tout passé?

04.08.09 - 22:39

15. Brenda :

Bonjour,

Le captcha est superbe. Je ne connaissais pas ce site qui est tres riche en informations.

23.08.09 - 13:49

16. emmamation :

merci j’en avais besoin

06.09.09 - 8:55

17. Tutoriels informatiques pour débutants :

Très bien fait ce captcha, en plus, chapeau pour les explications ! Pour une fois que je comprends comment fonctionne un captcha ! En plus il a l’air quand même assez complexe pour ne pas savoir être lu par des ordinateurs.

Merci beaucoup !

27.09.09 - 0:23

18. fabrice88 :

bonjour,
j’ai toujours l’erreur qui me dit que le code est incorrect.
par contre si je rentre aucun code j’ai bien le message qui me dit qu’auncun code n’a été saisie.

pouvez vous m’aider a résoudre ce probleme ?

11.10.09 - 10:33

19. Topazz :

Génial ce script ! Marche nickel chez moi, merci !

26.11.09 - 13:10

20. krol17 :

J’ai le même problème, le tuto est très bien mais la variable de sessions n’arrive pas à passé.
J’ai bien mit « session_start();»  en haut de chaque page.
est ce que quelqu’un sait ou le problème peut venir ?

D’avance merci

27.11.09 - 17:28

21. oussama :

si ca marche pas chez qlq faudra changer le type de decodage de fichier , moi j utiluse ANSi ou unicode et ts marche bien , merci

30.11.09 - 14:18

22. William MARTIN :

Bonjour,

les deux fonts utilisées sont sous quel licences ?

Merci

21.12.09 - 11:22

23. Vincent :

bonjour,

D’abord merci pour ce tuto très bien expliqué.
Mais j’aurai une question

Comment je l’intègre a une script d’enregistrement ?
http://surviveordie.no-ip.org/Register.php
Celui ci en l’occurence !

Merci d’avance

26.12.09 - 22:25

24. Xjames :

Bonsoir,
SVP, j’ai utilisé le code mais l’image ne s’affiche pas. je pense que y a un probleme de chemin mais je sai pas coment resoudre.
mon fichier captcha se trouve dans declaration/recap/captcha.php et il est appeler dans index.php qui se trouve dans declaration/recap/index.php
je ne sais pas comment gerer le probleme de chemin.
merci de votre aide

05.01.10 - 18:54

25. Thomeuxe :

Wouah !!

Magnifique !!

Je m’en servirais sur mon site ;)

24.01.10 - 19:29

26. Gb1963 :

Bonjour,
j’essaye de mettre en pratique votre captcha.
J’ai respecter vos consignes, copier vos codes et mis dans page captcha.php et index.php, copier l’image captcha.png.
Quand j’appelle ma page, j’obtiens à la place de l’image un carré blanc avec une croix. Je fais le test sur un serveur de Free et d’OVH et j’ai le même problème. Auriez-vous une idée pour résoudre mon problème? Je ne vois pas ce que j’ai raté.
Merci pour votre réponse.

06.02.10 - 13:29

27. Siloa :

Bonsoir.

J’ai inséré votre captcha dans mon formulaire d’inscription et il fonctionne à merveille.

J’ai juste une petite question. Vous mettez comme code :
.
Je ne comprends pas à quoi sert cet id=» captcha» , bien que si on l’enlève le refresh ne fonctionne plus.

J’aimerais mettre 2 captcha sur 1 même page, la présence de l’id m’en empêche et je trouve ça bien dommage.
Que faire pour remplacer id par class ?

Merci

06.02.10 - 23:42

28. Débutant93 :

Bonjour tout le monde
J’ai le même problème que GB1963. Mon site est héberger chez free… donc y a-t-il une solution?
L’images ne s’affiche pas ( et à la place y a un carré blanc et une croix en rouge – comme si l’image n’existe pas-)
PS: Les dimensions de mon image : 150*50

Merci à vous!

10.03.10 - 15:33

29. Movie-at-home :

Super tuto!

Merci beaucoup pour ton travail ;)

Bonne continuation :D

12.03.10 - 20:55

30. B3 :

« Bonsoir,
SVP, j’ai utilisé le code mais l’image ne s’affiche pas. je pense que y a un probleme de chemin mais je sai pas coment resoudre.
mon fichier captcha se trouve dans declaration/recap/captcha.php et il est appeler dans index.php qui se trouve dans declaration/recap/index.php
je ne sais pas comment gerer le probleme de chemin.
merci de votre aide» 

Pareil..

12.04.10 - 17:51

31. Christophe :

Bonjour et merci pour ce code efficace et bien commenté,

j’ai simplement modifié les couleurs possibles des lettres pour les assombrir et les faire un peu plus ressortir du fond.

et si quelqu’un a une explication pour l’ajout du paramètre id=Math.round(Math.random(0)*1000)+1 pour le changement à la volée je suis preneur

bonne continuation

11.05.10 - 13:35

32. guitarisk :

très très intéressant
merci beaucoup

16.05.10 - 2:13

33. lecocq17 :

Très bon soft
Je vais m’en servir pour remplacer le captcha existant dont l’image est fixe donc vulnérable
Merci au concepteur

03.06.10 - 15:26

34. aoe2010 :

Bonjour,

Je chercher un captcha en php, votre solution est très bien.
Ce pendant, j’ai conçu un formulaire en php avec un test champ en Javascript et j’ai donc mi votre système captcha.

Je dois certainement avoir la condition qui ne fonctionne pas, car que je mes le code captcha ou pas il valide mon formulaire.
J’ai même intégré le test en JS si le champs pas rempli, mais même en mettant autre lettre ou rien il passe outre.

Quelqu’un a une solution ?

26.06.10 - 23:29

35. aoe2010 :

(je cherchais) désolé il est tard. :)

26.06.10 - 23:30

36. maxime :

Merci c est tres pratique ;)

29.06.10 - 11:27

37. Kwep :

Très bon script, cependant il est totalement inutile de hasher le captcha code en md5 puisqu’il est stocké dans la session, côté serveur, donc totalement invisible pour le visiteur! En revanche le hashage md5 pourrait être utile si on veut se passer des sessions: on stock le captcha code directement dans le code HTML du formulaire via un input type=» hidden» , bien sur on hash le captcha code en md5 sinon ce serait trop facile (le bot verrait le code en claire dans la source de la page). Dans ce cas je conseil même un double hash (md5+ash1) car avec les rainbow table md5 un captcha code de 5 caractères serait déchiffré très rapidement.

18.07.10 - 16:51