15 09 2013
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.
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 :
//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) :
<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-
Utiliser Entity Framework pour les Procédures Stockées POO : Inclure des classes en PHP 5 via autoload
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
Je viens de corriger en mettant « $len = strlen( utf8_decode( $word ) ); » au lieu de « $len = mb_strlen($word, « UTF8″); »
Merci 🙂
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!
I truly prize your function, Wonderful post.
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+
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.