1
178

Protéger un formulaire avec un captcha

Créez site10 en copiant site9.

  1. /cms
    1. ...
    2. site9
    3. site10

Dans ce chapitre, nous allons programmer un moyen de protéger le site contre des robots qui essayent d'utiliser un formulaire.

Lisez l'article Envoyer une requête HTTP à un service web pour apprendre à écrire un programme qui exploite une application distante.

Pour tester le résultat en ligne, entrez http://www.frasq.org/cms/site10 dans la barre d'adresse de votre navigateur. Remplissez le formulaire en copiant le captcha dans la zone de saisie à droite de l'image. Envoyez le formulaire. Essayez d'envoyer le formulaire sans copier le captcha.

Commencez par créer une action appelée captcha en ajoutant le fichier captcha.php dans le dossier actions avec le contenu suivant :

  1. /cms/site10
    1. actions
      1. captcha.php
  1. require_once 'strrand.php';
  2. require_once 'strtag.php';
  3.  
  4. function captcha($lang) {
  5.     $charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
  6.  
  7.     $code = strrand($charset, 4);
  8.  
  9.     $_SESSION['captcha'] = $code;
  10.    
  11.     $img = strtag($code);
  12.  
  13.     header('Content-Type: image/png');
  14.     header("Content-Disposition: inline; filename=captcha.png");
  15.  
  16.     imagepng($img);
  17.     imagedestroy($img);
  18.  
  19.     return false;
  20. }

L'action captcha génère un code aléatoire de 4 lettres majuscules, le mémorise dans la session et retourne un document contenant une image qui affiche le code.

Pour donner accès à l'action captcha, ajoutez un alias dans le fichier includes/aliases.inc :

  1. $aliases = array(
  2.     array(
  3.         'captcha'               => 'captcha',
  4.     ),

Remarquez que l'alias ne dépend pas de la langue.

Ajoutez les fichiers strrand.php et strtag.php dans le dossier library avec les contenus suivants :

  1. function strrand($charset, $len) {
  2.     $max=strlen($charset)-1;
  3.     $s = '';
  4.  
  5.     srand();
  6.  
  7.     for ($i=0; $i < $len; $i++) {
  8.         $s .= $charset[rand(0, $max)];
  9.     }
  10.  
  11.     return $s;
  12. }

strrand retourne une suite aléatoire de caractères pris dans $charset de longueur $len.

  1. function strtag($text) {
  2.     $len=strlen($text);
  3.  
  4.     $fontfile=ROOT_DIR  . DIRECTORY_SEPARATOR . 'font.ttf';
  5.     $fontsize=24.0;
  6.  
  7.     $bbox = imageftbbox($fontsize, 0, $fontfile, $text);
  8.  
  9.     $w=$bbox[2]+($len-1)*20;
  10.     $h=40;
  11.  
  12.     $img = @imagecreatetruecolor($w, $h) or die();
  13.  
  14.     $bg=imagecolorallocate($img, 255, 255, 224);
  15.     $fg=imagecolorallocate($img, 64, 64, 64);
  16.  
  17.     imagefill($img, 0, 0, $bg);
  18.  
  19.     // print text unevenly
  20.     for ($x=15, $i=0; $i<$len; $i++) {
  21.         $y = rand($h/2,$h/2+15);
  22.         $r = rand(-45, 45);
  23.         imagettftext($img, $fontsize, $r, $x, $y, $fg, $fontfile, $text[$i]);
  24.         $x += rand(25, 35);
  25.     }
  26.  
  27.     // blur with colored dots
  28.     for ($i=0; $i<$w*$h/2.0; $i++) {
  29.         $color=imagecolorallocate($img, rand(128,255), rand(128,255), rand(128,255));
  30.         imagesetpixel($img, rand(0,$w-1), rand(0,$h-1), $color);
  31.     }
  32.  
  33.     return $img;
  34. }

strtag retourne une image piquée par des points de couleurs aléatoires contenant les caractères de $text suffisamment déplacés et tournés pour rendre le texte impossible à interpréter par un programme mais toujours lisible par un humain.

Installez le fichier d'une police de caractères au format TTF directement dans le répertoire racine du site :

  1. /cms/site10
    1. font.ttf

Entrez http://localhost/cms/site10/captcha dans la barre d'adresse de votre navigateur pour afficher le captcha. Appuyez sur F5 pour en afficher un autre.

Pour ajouter un captcha à la page de contact, modifiez le fichier mailme.phtml du dossier views/fr :

  1. <p class="input">
  2. <img class="captcha" src="<?php echo $base_path; ?>/captcha/mailme" alt="" title="Code de vérification" />
  3. &nbsp;:&nbsp;
  4. <input type="text" name="mailme_code" id="mailme_code" size="4" maxlength="4" title="4 lettres" onkeypress="return submitonenter(event, 'mailme_send')" value="" />
  5. </p>

Ajoute le champ de saisie mailme_code précédé d'un captcha.

  1. <?php if ($missing_code): ?>
  2. <p>Entrez le code de vérification affiché dans l'image.</p>
  3. <?php elseif ($bad_code): ?>
  4. <p>Le code de vérification est incorrect.</p>
  5. <?php endif; ?>

Affiche un message d'erreur si le code affiché par le captcha n'a pas été saisi ou s'il ne correspond pas au contenu de l'image.

Modifiez aussi la version en anglais :

  1. <p class="input">
  2. <img class="captcha" src="<?php echo $base_path; ?>/captcha/mailme" alt="" title="Verification code" />
  3. &nbsp;:&nbsp;
  4. <input type="text" name="mailme_code" id="mailme_code" size="4" maxlength="4" title="4 letters" onkeypress="return submitonenter(event, 'mailme_send')" value="" />
  5. </p>

Remarquez qu'appuyer sur Entrée dans le champ de saisie du code envoie le formulaire.

  1. <?php if ($missing_code): ?>
  2. <p>Enter the verification code displayed in the image.</p>
  3. <?php elseif ($bad_code): ?>
  4. <p>The verification code is incorrect.</p>
  5. <?php endif; ?>

Modifiez la fonction mailme dans le fichier blocks/mailme.php pour ajouter l'analyse du captcha :

  1.     $mail=$subject=$message=$code=false;

Initialise la variable qui contiendra le code renvoyé par le formulaire.

  1.             if (isset($_POST['mailme_code'])) {
  2.                 $code=readarg($_POST['mailme_code'], true);
  3.             }

Lit le code renvoyé par le formulaire.

  1.     $missing_code=false;
  2.     $bad_code=false;

Initialise les drapeaux d'erreur.

  1.         case 'send':
  2.             if (!$code) {
  3.                 $missing_code=true;
  4.                 break;
  5.             }
  6.             $captcha=isset($_SESSION['captcha']) ? $_SESSION['captcha'] : false;
  7.             if (!$captcha or $captcha != strtoupper($code)) {
  8.                 $bad_code=true;
  9.                 break;
  10.             }

Vérifie que le code a bien été transmis et qu'il correspond à la valeur mémorisée dans la session.

  1.         case 'send':
  2.             if ($missing_code or $bad_code or $bad_mail or $missing_subject or $bad_subject or $missing_message) {
  3.                 break;
  4.             }

N'effectue aucun traitement si le code est manquant ou incorrect. IMPORTANT : Le programme doit sortir au plus vite si le captcha est invalide.

  1.     $errors = compact('missing_code', 'bad_code', 'missing_mail', 'bad_mail', 'missing_subject', 'bad_subject', 'missing_message', 'internal_error');

Ajoute les drapeaux d'erreur $missing_code et $bad_code au contexte de la vue.

Modifiez la fonction bootstrap dans le fichier library/bootstrap.php pour ajouter l'ouverture d'une session :

  1. require_once 'session.php';
  2.  
  3. function bootstrap() {
  4.     global $base_url, $base_path, $base_root;
  5.     global $session_name;

Charge le code qui ouvre une session. Déclare la variable globale qui contient le nom de la session.

  1.     if (!isset($session_name)) {
  2.         list( , $session_name) = explode('://', $base_url, 2);
  3.         $session_name = 'FQ' . $session_name;
  4.  
  5.         if (ini_get('session.cookie_secure')) {
  6.             $session_name .= 'SSL';
  7.         }
  8.     }
  9.  
  10.     session_name(md5($session_name));
  11.     session_open();

Fabrique un nom de session unique. Définit le nom de la session pour PHP. Notez que si le nom de la session a déjà été défini, typiquement dans le fichier de configuration, il est préservé.

Ajoutez le fichier session.php dans le dossier library avec le contenu suivant :

  1. function session_reopen() {
  2.     session_close();
  3.     session_open();
  4. }
  5.  
  6. function session_open() {
  7.     session_start();
  8. }
  9.  
  10. function session_close() {
  11.     session_destroy();
  12.     $_SESSION=array();
  13. }

Ces 3 fonctions gèrent l'ouverture et la fermeture de la session. IMPORTANT : Le nom de la session a été défini au préalable par la fonction bootstrap.

Entrez http://localhost/cms/site10/fr/contact dans la barre d'adresse de votre navigateur. Vérifiez que le formulaire fonctionne si le code dans le captcha est correct, manquant ou invalide.

Commentaires

Votre commentaire :
[p] [b] [i] [u] [s] [quote] [pre] [br] [code] [url] [email] strip aide 2000

Entrez un maximum de 2000 caractères.
Améliorez la présentation de votre texte avec les balises de formatage suivantes :
[p]paragraphe[/p], [b]gras[/b], [i]italique[/i], [u]souligné[/u], [s]barré[/s], [quote]citation[/quote], [pre]tel quel[/pre], [br]à la ligne,
[url]http://www.izend.org[/url], [url=http://www.izend.org]site[/url], [email]izend@izend.org[/email], [email=izend@izend.org]izend[/email],
[code]commande[/code], [code=langage]code source en c, java, php, html, javascript, xml, css, sql, bash, dos, make, etc.[/code].