par / LinkedIn 6 mn

Un formulaire web Anti Spam ? Sans CAPTCHA, merci !

Un formulaire Anti Spam, protection anti-robots

En tant qu’administrateur ou éditeur de site web il vous arrive sûrement de recevoir des messages en provenance du formulaire de contact présent sur votre site qui vous sont envoyés, non pas par des utilisateurs “humains”, mais par des robots qui remplissent ces formulaires automatiquement avec de la pub, du code et des liens.

Ces robots trouvent et analysent le formulaire pour ensuite le soumettre automatiquement. Leur projet consiste à tenter de poster des liens dans les forums en espérant augmenter le pagerank de leur site, ou encore à injecter du code PHP ou des commandes mySQL pour altérer le comportement de votre site…

Quelle que soit l’intention, vous recevez des dizaines de messages indésirables dont vous devez endiguer le flot. LA solution à laquelle on pense souvent est d’ajouter ce qu’on appelle un “CAPTCHA“, une image affichée à l’ecran dont il faut reproduire le contenu dans un champ du formulaire. Le CAPTCHA n’est pas forcement une solution satisfaisante, d’autant qu’il est souvent “cracké” par les robots en question (lire ici) et surtout, c’est peut-être une protection anti spam mais surtout un frein au remplissage du formulaire pour l’utilisateur humain…

Que faire pour protéger et sécuriser son formulaire ?

Il n’y a pas de solution fiable à 100% mais ces quelques améliorations dans le traitement du formulaire vont permettre d’endiguer le flot des messages indésirables postés par des scripts de soumission automatiques. Les techniques exposées ci-après sont complémentaires, l’application d’une seule, et même de toutes, n’est pas forcement efficace pour stopper toutes les formes de soumissons automatiques…

Le service minimum

Ad minima le formulaire, ou plutôt son traitement, réalise un minimum de contrôles de routine. D’abord coté client, en Javascript, et ensuite et surtout coté serveur, à l’arrivée des données.

  • On vérifie la présence de saisie dans les champs obligatoires, le cas échéant.
  • On vérifie la “validité” de l’adresse email envoyée, ou plutôt sa forme, sur le schéma : login@domaine.tld

Vérifier le referer

Chaque requête HTTP présente une information sur la page de provenance : le referer. Quand un script soumet un formulaire le referer peut ne pas être la page du formulaire, ce qui est le cas d’une soumission “humaine”. Vérifions en début de traitement le “referer” de la requête.

// Verification du referer, ici en PHP
// l’adresse du formulaire est ici http://www.mondomaine.tld/contact

if ($_SERVER[“HTTP_REFERER”] != “http://www.mondomaine.tld/contact”){
  $message = ‘Désolé, une erreur est survenue…’;
  return;
}

Pas d’URL ni de code dans le message !

Les robots postent des URL dans les zones de texte du formulaire pour faire de la pub et esperer augmenter le pagerank par exemple. Il tenteront aussi de placer du code dans leurs envois. Rejetons simplement toutes les soumissions d’URL et de toute forme de code dans le formulaire.
Si la fonction PHP ci-dessous renvoi TRUE, on cesse le traitement et on affiche un message du genre : “Certains caractères ne sont pas autorisés dans le message”.

// une fonction (PHP) pour tester le contenu d’un champ

public function detectTags($string){

// Pas de code de la forme <tag attr=XXX>texte</tag>
$testHTML = preg_match(“/<[^>]+>/”, $string);

// Pas de code de la forme [tag attr=XXX]
$testTag= preg_match(“/[[^>]+]/”, $string);

// pas de lien commençant par http://…
$testURL = strstr($string, “http://”);

if(($testHTML || $testTag) || $testURL ){
  return true;
}

Tester le Javascript et la soumission de champs cachés à l’affichage

Nos robots ne remplissent pas les formulaires, ils envoient directement le contenu à l’adresse de traitement définie dans la balise HTML <FORM>. Le javascript des pages n’est pas exécuté à la soumission.
Mettons en place 3 champs de type “text”, cachés avec les CSS de la page.

  • L’un d’entre eux contient une valeur
  • Le deuxième sera rempli avec Javascript à la soumission par la valeur du premier
  • Le troisième est vide… Et devra le rester !

// les champs “tests” du formulaire

<input style=“display:none;” type=“text” id=“test1” name=“test1” value=“hello”>
<input style=“display:none;” type=“text” id=“test2” name=“test2” value=”“>
<input style=“display:none;” type=“text” id=“email2” name=“email2” value=”“>

// On rempli le deuxieme champ avec la valeur du premier
// ici avec jQuery, l’id du formulaire est formContact

$( ‘#formContact’ ).submit(function() {
  $( ‘#test2’ ).val($( ‘#test1’ ).val());
});

Coté serveur on procède à nos vérifications pour éventuellement afficher un message, par exemple : “Désolé, une erreur est survenue ! Vérifiez que Javascript est activé pour votre navigateur…”…

// En PHP, test sur les valeurs de nos champs…
// ici sous la forme d’une conditionnelle, on peut aussi imaginer une fonction…

if (
(($_POST[‘test1’] != ‘hello’)
|| ($_POST[‘comment2’] != ‘hello’))
|| ($_POST[‘email2’] != ‘’)
){

  // Ici le code à exécuter si vrai…
}

Mon robot prendra bien un petit cookie ?!

Qu’il ne saura pas lire… L’idée est ici de déclarer une variable aléatoire que l’on stocke dans la session en cours à l’arrivée sur la page, mais aussi dans un champ de type “hidden” du formulaire. Lors du traitement coté serveur on compare la valeur stockée en session avec la valeur envoyée par le formulaire. Les scripts de soumisson automatique ne gèrent pas les sessions, la valeur retournée sera vide. Nous ne traitons pas le formulaire dans ce cas … Nous afficherons un message à l’écran pour la forme, genre : “Désolé, une erreur est survenue !

// En PHP. Création d’une valeur pour le test…
// enregistrement en session

$testVal = md5(uniqid(microtime(), true));
$_SESSION[$form.’_testVal’] = $testVal;


// Dans le formulaire, la valeur à poster dans un champ

<input type=“hidden” name=“testVal” value=”<?= $testVal ?>” />

// Test au traitement du formulaire

if (
!isset($_SESSION[$form.’_testVal’]))
|| !isset($_POST[‘testVal’]))
|| $_SESSION[$form.’_testVal’] !== $_POST[‘testVal’])
){

  // Ici le code à exécuter si vrai…
}

Mon formulaire “bullet-proof” * ?!

Contrôles coté client, dans le navigateur, coté serveur, à la réception des données. Tests sur les champs obligatoires, vérification de la forme d’une adresse email, vérification de la présence de code, ou d’URL… Test du referer, du Javascript, avec des champs cachés, des cookies de session…

Voilà un arsenal de bonnes pratiques à mettre en place pour la gestion des formulaires dans les sites web, en se passant du controversé CAPTCHA… Tout cela pour déterminer si le formulaire est posté par un utilisateur humain.

Des astuces pour endiguer le flot de soumissions automatiques et indésirables du formulaire qui assurément ne saurait être 100% “bullet-proof” ! Mais qui sans conteste peuvent gérer une majorité des cas.
Enjoy.

Lire aussi :

Formulaires Web, le Captcha comme antispam : la panacée ?

* Bullet-proof : A l’épreuve des balles