Blog de Portekoi : Programmation et Astuces

Programmation Cobol, Php, Asp, Java et DotNet…

Captcha simple mais efficace en PHP

Le pourquoi du comment :

Sur certains de mes sites, j’utilisais un captcha sous cette forme :

Combien font 1 + 3?

Mais les robots trouvaient parfois la bonne réponse sachant que cela n’était que des chiffres entre 1 et 18 maximum.

J’ai donc recréé un autre système plus performant composé de deux parties : La lettre dans un mot choisit de manière aléatoire et la présence d’un champ hidden « piégé »(1).
Pour la première solution, on le voit assez souvent sur le web et je trouve cela assez efficace mais j’ai préféré doubler la sécurité avec un champ caché.

Cela nous donnera donc ceci :

Quelle est la 2ième lettre du mot : Ordinateur

Pour pouvoir valider le formulaire, il faudra bien évidemment répondre « r ».

Comment cela fonctionne-t-il?

Tout d’abord, dans votre formulaire, placez un champ piégé (comme ci-dessous) bien nommé comme « email » ou « login » par exemple. Les robots raffolent de ce genre de données.
Ensuite, dans le script qui contrôlera les données, il suffira de vérifier que le champ « email » (ou « login ») soit bien vide.
S’il devait être saisi, c’est qu’un robot sera à l’origine de la requête.

<input type="hidden" name="email" value="" />

Ensuite, pour le contrôle via une lettre, j’ai d’abord recherché un site me permettant d’obtenir une liste de mots conséquente. J’ai trouvé celui-ci :
http://www.liste-de-mots.com/

J’ai ensuite récupéré ma liste de mots et tout placé dans un fichier texte avec un encodage en UTF-8. Ce point est très important.

Voici le code php pour récupérer le mot et déterminer une lettre de manière aléatoire :

<?php
//si le fichier existe
if(file_exists("fichier.txt")){
    //On lit le fichier
    $f_contents = file("fichier.txt");
    //On en extrait un ligne et donc un mot en remplaçant les sauts de ligne
    $word = str_replace("%0D%0A", "", trim($f_contents[array_rand($f_contents)]));
    //Longueur du mot?
    $len = strlen( utf8_decode( $word ) );
    //Nombre aléatoire entre 1 et longueur du mot.
    $nb = rand(1, $len);
    //On récupère la lettre pour la réponse
    //(-1 car la fonction substr commence à l'indice 0)
    $reply = substr($word, $nb-1, 1);  
    //On place la réponse dans une session :
    $_SESSION["securite"] = $reply;
   
    /*
    Ainsi, dans la page qui fera le contrôle, il suffira de faire entre autres contrôles :
    if($_SESSION["securite"] == $_POST["securite"] && $_POST["email"] == ""){
        //On envoi le message  
    }
    */

}
?>

Le code HTML (via Bootstrap 3) :

<form role="form">
<div class="row">
<div class="form-group col-lg-4"><label for="email">Email</label>
<input class="form-control" id="adreml" type="email" name="adreml" placeholder="Saisissez votre Email" /></div>
</div>
<div class="row">
<div class="form-group col-lg-4"><label for="prenom">Prénom</label>
<input class="form-control" id="prenom" type="text" name="prenom" placeholder="Votre Prénom" /></div>
</div>
<div class="row">
<div class="form-group col-lg-4"><label for="message">Message</label>
<textarea class="form-control" id="message" name="message" placeholder="Votre Message" rows="6"></textarea></div>
</div>
<div class="row">
<div class="form-group col-lg-4"><label for="securite">Quelle est la <?php echo $nb;?>ième lettre du mot : <?php echo $word;?></label>
<!-- Je n'ai pas trouvé comment faire pour mettre un type "small" comme dans BootStrap 2.3.2 -->
<span class="col-lg-4">
<input class="form-control" id="securite" type="text" name="securite" />
</span></div>
</div>
<input type="hidden" name="email" value="" />
<button class="btn btn-default" type="submit">Envoyer</button>

</form>

La source complète est ici : http://blog.portekoi.com/exemple/captcha/

Le fichier contenant les mots :
http://blog.portekoi.com/exemple/captcha/fichier.txt

(1)Champ caché détaillé ici : http://www.fredboucher.com/posts/view/un-captcha-infaillible-

, ,

6 commentaires sur “Captcha simple mais efficace en PHP

  • Telex dit :

    Il y a une erreur: parfois il demande une position supérieure au nombre de lettres disponibles.
    Par exemple: la 11ème lettre du mot ciselasses

    • Portekoi dit :

      Je viens de corriger en mettant « $len = strlen( utf8_decode( $word ) ); » au lieu de « $len = mb_strlen($word, « UTF8″); »
      Merci 🙂

  • David dit :

    Perso j’ai trouvé une solution encore plus efficace qu’un champ caché dans un formulaire. Mon fichier random.php qui génère le code captcha et l’image, crée aussi une deuxième variable dans $_SESSION qui contient une clé privée encodée en dur dans le code PHP. Il est ensuite très facile de vérifier dans un autre fihier.php que la valeur encodée pour la captcha correspond et aussi et surtout de valider que la clé est bien présente (crée par random.php) et correcte. Cette clé sera toujours invisible côté client. Si ce n’est pas la cas, c’est clairement une tentative de hack et dans les deux cas, return=false et rien n’est soumis dans une éventuelle DB;
    Bonne progra!

  • Johnb750 dit :

    I truly prize your function, Wonderful post.

  • lami dit :

    Bonjour,

    A la longue on voi le chiffre 2 et le mot Ordinateur, on déduit très rapidement que c’est r… pour un chinois qui souhaite vous bombarder de mail..

    Si on remplace le chiffre 2 par deux, il est obliger de traduire ce qui lui prend plus de temps..

    a+

    • Portekoi dit :

      Bonjour,

      Que voulez-vous dire? Le truc, c’est que ce n’est pas toujours le chiffre ‘2’ pour le mot ordinateur car c’est aléatoire.

      C’est justement le but de ce code.

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *