Mettez en œuvre les captchas avec Zend Framework

Pierre Schwartz

Cet article va présenter la problématique d'utilisation d'un captcha, puis plusieurs solutions de mise en œuvre dans un environnement Php/Zend.

Commentez

- Titre : Mettez en œuvre les captchas avec Zend Framework

- Auteur : Pierre Schwartz

- Parution : 27 octobre 2010

- Mise à jour : 4 février 2020

- Licence : Les sources présentées sur cette page sont libres de droits et vous pouvez les utiliser à votre convenance. Par contre, la page de présentation constitue une œuvre intellectuelle protégée par les droits d'auteur. Copyright ® 2010 Pierre Schwartz. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Un captcha, pour quoi faire ?

- II. Zend_Captcha

- II-A. Zend_Captcha_Figlet

- II-B. Zend_Captcha_Image

- II-C. Zend_Captcha_reCaptcha

- II-D. Captchas avec Zend_Form

- III. Conclusion

I - Un captcha, pour quoi faire ?

Dès lors que vous publiez une page Internet, un processus automatisé sera en mesure d'en parcourir toutes les pages, de soumettre tous les formulaires et de suivre tous les liens. Un robot sera ainsi en capacité de se créer un compte sur une application en ligne, de poster du contenu éventuellement malveillant, de spammer les autres membres… bref, de compromettre votre application.

Une rapide réflexion nous amène à la conclusion qu'il faut trouver un moyen de s'assurer qu'un visiteur est bien un visiteur « humain » et non pas un « robot », ce qui revient à établir un test de Turing. La première solution qui a permis, dans une certaine mesure, de valider ce test était de poser une question simplissime au visiteur, par exemple le résultat d'une opération mathématique 1+3=?. En générant une opération mathématique simple et aléatoire pour chaque visiteur, en la stockant en session avec son résultat, on était en mesure de vérifier que la soumission d'un formulaire possédait la bonne réponse et donc qu'elle avait été initiée par un être humain.

Ce premier essai montre cependant très vite ses limites puisqu'un robot est capable de repérer cette protection dans un formulaire, d'interpréter cette opération mathématique, de la résoudre et de renvoyer le résultat dans un formulaire, se faisant passer pour plus humain qu'il n'est. De la même manière, on s'est rendu compte que toute question basée sur une information textuelle pouvait être récupérée et résolue automatiquement.

L'idée maitresse est de fournir un « objet » ne pouvant être interprété que par un humain. Et pour faciliter la vérification de cet « objet » par votre application, l'idée est de représenter du texte de manière suffisamment déformée. Dans cette description assez vague on retrouve le texte mis en image et le texte mis en ASCII art, solutions que je vais présenter.

Certes vous pourriez essayer de générer vous-même des captchas en manipulant une image, en rendant un texte aléatoire dans cette image, en ajoutant des déformations et du bruit, mais ces opérations sont déjà existantes dans Zend. :)

II - Zend_Captcha

Le framework Zend fournit plusieurs solutions de gestion et de vérification de Captcha. Zend amène les objets de base pour gérer les captchas, mais amène aussi de quoi les intégrer dans un formulaire Zend_Form. Tous les types de captchas gérés par Zend implémentent Zend_Captcha_Adapter et peuvent facilement être intégrés dans un Zend_Form.

Zend Framework propose des solutions pour gérer des captchas de plusieurs types :

- un mot à repérer dans une image déformée ;

- un mot représenté en ASCII art ;

- un mot à renvoyer inversé ;

- un mot à repérer dans un captcha recaptcha.

La récupération du mot inversé n'est pas une technique fiable pour valider un test de Turing, nous ne nous y étendrons pas.

Je vais présenter des cas d'utilisation pour les trois autres types de captchas gérés par Zend. Implémentant les mêmes interfaces, leur utilisation sera très proche. Comme pour tous les types Zend_Captcha_*, le fonctionnement reste le même : instanciation du captcha avec le bon paramétrage, insertion du captcha dans le code de rendu html, vérification lors de la soumission d'une requête.

Voilà le schéma UML d'organisation des différents types de captchas Zend :

Voilà le schéma global des exemples que je vais présenter :

<html>
<head></head>
<body>

<?php

require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->registerNamespace('Zend_');

// instanciation du captcha à utiliser
$captcha = new ...
// paramétrage du captcha
$captcha-> ...

if (!isset($_POST['captcha'])){
// premier affichage, on génère un captcha

$captcha->generate();
}else{

// une réponse est proposée pour la résolution du captcha

// vérification du captcha
if ($captcha->isValid( ... )){
echo '<img src="ok.gif" />';
$captcha->generate();
}else{
echo '<img src="ko.png"/>';
$captcha->generate();
}
}
?>

<form method="post">

<?php echo $captcha->render () ?>

<input type="hidden" name="captcha[id]" value="<?php echo $captcha->getId() ?>" size="40" /><br/>
<input type="text" name="captcha[input]" size="40" /><br/>
<input type="submit"/>

</form>

</body></html>

II-A - Zend_Captcha_Figlet

Cet objet, bien qu'anecdotique, permet de créer des captchas où le mot à repérer se trouve écrit en ASCII art. Voilà des exemples de rendus que l'on pourra obtenir :

Le développeur ne pourra pas paramétrer grand-chose : à part le nombre de lettres du captcha, rien de spectaculaire.

// instanciation et paramétrage
$captcha = new Zend_Captcha_Figlet();
$captcha ->setWordLen(7);

II-B - Zend_Captcha_Image

Voilà vraiment la classe qui permettra de générer des images déformées pour masquer du texte. Cet objet intègre du texte dans une image et y ajoute du bruit : des étoiles et des lignes. Le développeur a la possibilité de spécifier la taille (hauteur, largeur) de l'image générée, la police de caractères à utiliser, la taille du texte, le niveau de bruit des étoiles et le niveau de bruit des lignes. Zend_Captcha_Image utilise la bibliothèque GD pour générer les images, vous devez donc l'avoir installée sur le serveur web. Étant donné que les captchas seront générés puis stockés directement sur le serveur, cette solution de captchas est parfaitement utilisable dans le cas où le client ne serait présent que sur un intranet sans accès extérieur. En ce sens c'est la solution la mieux maitrisée puisque le serveur a toutes les clefs : la génération du captcha et sa validation.

Voici quelques exemples de captchas faisant varier le niveau de bruit des étoiles et des lignes :

lignes : 0

lignes : 2

lignes : 20

points : 0

points : 20

points : 100

points : 1000

Attention cependant au choix de la police. Bien que le but d'un captcha étant de masquer un texte et de le rendre peu (auto)lisible, il ne faut pas tomber dans l'excès d'un camouflage trop important. Il suffit de générer un captcha avec une police cursive (Mistral par exemple) pour obtenir des captchas vraiment peu lisibles même par un être humain. Bon courage :

Les autres paramètres du Zend_Captcha_Image concernent l'emplacement de stockage des captchas générés et n'offrent que peu d'intérêt pédagogique. Voici le code illustrant l'utilisation de Zend_Captcha_Image, toujours selon notre template défini plus haut.

<?php

require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->registerNamespace('Zend_');

// instanciation, paramétrage
$captcha = new Zend_Captcha_Image();
$captcha ->setWordLen(8)
->setHeight(100)
->setWidth(300)
->setFont("./tahoma.ttf")
->setFontSize(50)
->setSuffix(".png")
->setImgDir("out/")
->setImgUrl("out")
;

if (!isset($_POST['captcha'])){
$captcha->generate();
}else{

// vérification

if ($captcha->isValid($_POST['captcha'])){
echo '<img src="ok.gif" />';
$captcha->generate();
}else{
$captcha->generate();
echo '<img src="ko.png"/>';
}
}
?>
<form method="post">

<img src="<?php echo $captcha->getImgUrl () . $captcha->getId() ?>.png"/><br/>

<input type="hidden" name="captcha[id]" value="<?php echo $captcha->getId() ?>" size="40" /><br/>
<input type="text" name="captcha[input]" size="40" /><br/>
<input type="submit"/>
</form>

Toutes les images générées seront ici stockées dans le dossier out/. Zend_Captcha_Image intègre une logique de garbage collector et passe automatiquement régulièrement pour vider le contenu de ce dossier. La fonction setGcFreq permet de spécifier à quelle fréquence de requêtes le garbage collector doit être appelé.

On notera que la vérification du captcha nécessite l'id du captcha généré ainsi que le mot repéré qui doivent être passés dans un même paramètre HTTP.

II-C - Zend_Captcha_reCaptcha

Ce système de captcha utilise le service web reCAPTCHA (racheté par Google) pour générer et vérifier le texte à reconnaître. Les images proposées à la reconnaissance de mots proviennent d'éléments non reconnus dans une analyse automatique de caractères. En résolvant des captchas reCAPTCHA, vous participez à la numérisation d'ouvrages qui n'ont pu être entièrement numérisés. Vous avez toujours deux mots à reconnaître : l'un d'eux est connu (c'est sur celui-ci que se fera la validation) et l'autre est à déchiffrer : c'est la participation à la numérisation.

Pour pouvoir utiliser le service reCAPTCHA, vous devez créer des clefs d'authentification publique et privée. Pour cela rendez-vous sur le site https://www.google.com/recaptcha/admin/create puis générer une paire de clefs pour le domaine web de votre application. Il n'est pas rare de devoir générer une paire de clefs en mode développement si vous travaillez sur un domaine *.localhost et une autre paire de clefs pour la mise en production effective de votre application.

L'utilisation de reCAPTCHA nécessite que le client ait accès à Internet puisque les captchas seront directement fournis par le service reCAPTCHA. Le serveur devra lui aussi être relié à Internet puisque c'est lui qui demandera à reCAPTCHA de vérifier si un mot donné résout ou non le captcha. Dans cette solution, le serveur ne fait ni la génération du captcha ni sa vérification, il ne connait jamais les mots cachés dans le captcha.

Comme pour les méthodes précédentes, la mise en œuvre est très simple :

<html>
<head>
</head>
<body><?php

require_once 'Zend/Loader/Autoloader.php';
Zend_Loader_Autoloader::getInstance()->registerNamespace('Zend_');

// instanciation
$captcha = new Zend_Service_ReCaptcha(
"votre clef publique",
"votre clef privée"
);
// paramétrage
$captcha->setOptions(array(
'theme'=>'clean',
'lang'=>'fr'
));

if (!isset($_POST['recaptcha_challenge_field'])){

}else{

// vérification
$captchaResult = $captcha->verify(
$_POST['recaptcha_challenge_field'],
$_POST['recaptcha_response_field']
);

if ($captchaResult->isValid()){
echo '<img src="ok.gif" />';
}else{
echo '<img src="ko.png"/>';
}
}
?>

<form method="post">

<?php echo $captcha->getHtml () ?>

<input type="submit"/>

</form>

</body>
</html>

On remarque que la vérification se fait sur des champs POST nommés recaptcha_challenge_field et recaptcha_response_field. Ces deux champs sont positionnés par l'outil reCAPTCHA et sont générés lors de l'appel à $captcha->getHtml(). Ce n'est pas à vous de les générer.

Le développeur n'a pas beaucoup de possibilités pour paramétrer le captcha reCAPTCHA : à part le look dudit captcha, pas grand-chose. Voici les quatre looks existants :

II-D - Captchas avec Zend_Form

Tous les captchas gérés par Zend implémentent Zend_Captcha_Adapter, ils peuvent ainsi tous êtes ajoutés à un Zend_Form de la même manière via un Zend_Form_Element_Captcha. Exemple :

$form = new Zend_Form();

// construction du Zend_Form avec tous les champs nécessaires
$form->addElement(...);
...

//  création du captcha
$captcha = new Zend_Form_Element_Captcha('captcha', array(
'label' => "Merci de confirmer que vous êtes humain",

// paramétrage en reprenant les noms de méthodes vus précédemment
'captcha' => array(
"captcha" => "Image",
"wordLen" => 8,
"font" => "./tahoma.ttf",
"height" => 100,
"width" => 300,
"fontSize" => 50,
"imgDir" => "out/",
"imgUrl" => "out/"
)
));

$form->addElement($captcha);

// suite du rendu du Zend_Form

La validation du formulaire se fera via $form->isValid($_POST), l'utilisation du captcha devient complètement transparente, le développeur n'a plus à la gérer manuellement.

III - Conclusion

Zend fournit les outils nécessaires à une gestion de captchas qui devrait convenir aux utilisations les plus courantes. Mais bien sûr, rien ne vous empêche de développer votre propre captcha héritant de Zend_Captcha_Base si vous ne trouvez pas votre bonheur.

Merci à l'équipe Web et à Mahefasoa pour leurs relectures technique et orthographique.