Un captcha super simple et efficace

Posté le Thu 08 November 2018 dans Projets
Temps de lecture estimé : 5 minute(s).

Le nombre de spams envoyés dans les commentaires de ce blog a ces dernières semaines totalement explosé. De 4 ou 5 par jour, je suis arrivé à plusieurs dizaines de commentaires indésirables chaque jour. Cela devenait insupportable, et je me suis donc mis en quête d'un système captcha, afin de limiter le nombre de commentaires indésirables.

La réflexion

J'ai du coup commencé à regarder du côté des systèmes captcha standards :

Le captcha standard

C'est celui qu'on voit le plus, où il faut recopier un texte inscrit dans une image. Il fonctionne plutôt bien. Cependant, il ne me plaît pas trop parce qu'il est parfois compliqué d'arriver à déchiffrer le texte. Et puis il est trop classique, on le voit partout.

Le captcha made in Google

C'est celui qu'on voit de plus en plus. Il s'agit d'une simple case qu'il faut cocher pour confirmer qu'on est pas un robot.

Parfois, si le fait de cocher la case ne suffit pas à identifier notre nature (robot ou humain), on nous demande alors d’identifier des objets sur des images. Par exemple, cocher toutes les images contenant un panneau de signalisation.

Même s'il est très efficace, je n'apprécie personnellement pas du tout ce système. Tout d'abord, je trouve que le système d'images est très mal fait. Il n'est parfois vraiment pas évident d’identifier ce qu'on nous demande. Par exemple, dans l'image ci-dessus, faut-il sélectionner les images contenant seulement une toute petite partie de panneau ?

Ensuite (et surtout), pour utiliser ce captcha, il faut inclure une api Google sur son site. Cette api va observer et évaluer notre comportement sur la page, afin de déterminer si on est un robot ou non. Pour cela, elle va suivre la position de la souris, étudier comment sont renseignés les différents champs, et je ne sais quoi d'autre encore...

Je ne souhaite pas ici entrer dans le débat de savoir si nous sommes traqués sur internet, mais tout de même, cela équivaut à peu près à avoir quelqu'un debout derrière notre écran qui regarde tout ce qu'on fait. Et pour cette raison, je ne voulais pas de ce système sur ce site.

Le captcha audio

Il faut ici écouter un message audio et le recopier dans un case. L'idée est plutôt intéressante, mais au final peu pratique. Surtout dans un lieu public, au boulot, sur son smartphone, etc...

Le captcha publicitaire

Pour prouver que l'on est pas un robot, il faut recopier le nom ou le slogan de la marque qui apparaît sur l'image. Cette version fera le bonheur des publicitaires, mais très peu pour moi...

Le captcha invisible

Il s'agit ici plus d'une ruse que d'un vrai captcha. Il suffit de placer sur la page un champ invisible. Un humain ne verra pas ce champ, et donc ne le remplira pas. Un robot remplira lui tous les champs avant de valider le formulaire. Si le champ a été rempli, cela signifie qu'on est en présence d'un robot.

L'idée est sympa, mais cependant beaucoup trop facile à contourner. Il suffit pour un robot de s'assurer que le champ est bien visible avant de le remplir, et, contrairement à la reconnaissance de texte ou d'image, c'est quelque-chose de vraiment facile à implémenter.

La solution

Finalement, j'ai fini par tomber sur une idée à la fois efficace et facile à mettre en place et à utiliser. Le captcha question/réponse. L'idée est simple : On pose une question dont la réponse est évidente pour un humain, et ce dernier doit écrire la réponse dans la zone de texte. Les questions doivent rester très simples, afin que tout le monde puisse y répondre. Voici quelques exemples :

  • Combien font 1 + 3 ?
  • Dans quel pays se situe Paris ?
  • Quelle est la version de Windows 10 ?
  • De quelle couleur est un cheval blanc ?

Après quelques longues minutes de réflexion intense, extrême et profonde, voire même parfois frénétique, j'ai fini par me décider pour la question suivante : Quel mois de l'année sommes-nous ?

Je trouve cette solution élégante. Elle fonctionne sur toutes les plateformes. Elle est ultra simple pour l'utilisateur. Elle est très simple également à mettre en place. Elle n’alourdit pas la page avec je ne sais quelle API externe. Elle ne traque pas le comportement de l'utilisateur.

Bref, elle me plaît bien. Il restait maintenant à l'implémenter.

L'implémentation

La mise en place ne fur pas très compliquée, et une petite heure plus tard, le code suivant répondait à mes attentes :

Côté client (HTML)

Simplement un nouveau champ dans mon formulaire :

< p class="captcha" >
    < label for="email" >Quel mois de l'année sommes nous ?< /label >
    < input name="captcha" id="captcha" type="text"
                       placeholder="Pour vérifier que vous êtes humain" value="" >
< /p >

Côté serveur (PHP)

Une première méthode permettant de récupérer le numéro du mois courant, et de faire la comparaison avec la valeur dans le captcha :

public static function checkCaptcha($comment) {
    $captcha = $comment['captcha'];
    $month = intval(date('n'));

    // Compare numbers
    if ($month == intval($captcha))
        return true;

    // Compare texts
    $monthNames = self::getMonthNames($month);

    if (in_array(mb_strtolower($captcha, 'UTF-8'), $monthNames)) {
        return true;
    }

    return false;
}

Une seconde méthode permettant de récupérer le nom du mois et ses différentes variantes à partir du numéro du mois :

private static function getMonthNames($monthNumber) {
    switch ($monthNumber) {
        case 1:
            return array("jan", "janvier", "january");
        case 2:
            return array("fev", "fevrier", "février", "february");
        case 3:
            return array("mar", "mars", "march");
        case 4:
            return array("avr", "avril", "april");
        case 5:
            return array("mai", "may");
        case 6:
            return array("jun", "juin", "june");
        case 7:
            return array("jul", "juillet", "july");
        case 8:
            return array("aout", "august");
        case 9:
            return array("sep", "septembre", "september");
        case 10:
            return array("oct", "octobre", "october");
        case 11:
            return array("nov", "novembre", "november");
        case 12:
            return array("dec", "decembre", "décembre", "december");
    }
}

Et enfin un petit code qui appelle la méthode de test :

if (!Comment::checkCaptcha($input)) 
{
    // Do something... or actually nothing here.
}

Voilà ! C'est tout !

Conclusion

Pour le moment, plus aucun spam. Zéro. Nothing. Nada. Nichts. Kitu.

Le rapport gain / temps investi est donc vraiment très bon !

Et ça fait bien plaisir !