Débarrasser drupal du SPAM sans captchas (Suite)
Le 28 octobre 2007, à 18:27 par Ulhume...

Dans un billet précédent, j'abordais une méthode assez simple pour se passer des Captchas basée sur une évaluation du temps de réponse à un formulaire. Totalement orientée "serveur", elle avait le mérite de ne rien demander au client. En revanche cette méthode présente deux défauts. Tout d'abord elle nous oblige à désactiver le cache pour les pages à protéger ce qui est très contraignant en terme de charge serveur. A noter que c'est aussi le cas pour tous les modules captchas disponible à l'heure actuelle. Ensuite, certain spammeurs sont assez malins, ou assez lents, pour mettre plus des 10 secondes fatidiques à répondre au formulaire, et ainsi passer le filtre (environ 10%).

Pour améliorer le système, une nouvelle approche consiste à exploiter le même concept de champ caché, mais en utilisant cette fois un peu de javascript côté client pour lui donner sa "bonne" valeur. Aucune méthode n'est fiable à 100% mais celle-ci semble s'en approcher.

Bilan et analyse

Tout d'abord un petit topo de ce que j'ai pu apprendre des spammers pendant cette semaine d'expérimentation :

  • Il semble y avoir deux familles de spammeurs (regroupés dans une plus large famille dite de "chieurs"). Ceux qui savent quel CMS vous utilisez (Drupal, Wordpress, DotClear, etc.) et qui forgent des réponses spécifiques. Ceux là sont vite déroutés si l'on insère un nouveau champ non prévu en standard ou si l'on change les urls d'accès. La deuxième famille est plus complexe à gérer car elle se moque du type de CMS et analyse la page pour forger le "bon" formulaire.
  • Contrairement à la légende en circulation, de nombreux spammeurs connaissent javascript et exécute très bien tous les scripts d'une page. Encoder un formulaire avec des document.write, même avec cryptage ne sert absolument à rien.
  • Ces mêmes logiciels spammeurs savent suivre les scripts même s'ils sont dans des fichiers externes. Donc fragmenter un formulaire en plusieurs fichiers .js n'est d'aucune aide.

En revanche ce que ne semble pas savoir faire cette dernière famille à problèmes, c'est déclencher des événements javascript (onfocus, onkeyup,etc..). En effet, si l'on place par exemple un événement onfocus sur un champ et un autre sur un autre champ, le malheureux n'a aucun moyen de savoir lequel serait "piégé" et lequel rendrait le formulaire valide.

L'idée ici est donc de reprendre cet concept (merci Advaita Wink pour modifier le champ caché de notre précédent article et lui donner une valeur "magique" à ce champ sur déclenchement d'un événement utilisateur onkeyup, rendant ainsi valide le formulaire.

Mise en œuvre

L'autre avantage de cette méthode est de ne plus avoir à désactiver le cache de page. En effet, il suffit de mettre dans le formulaire une valeur par défaut "fausse" et dans l'événement javascript la "bonne" pour le système fonctionne en cache ou pas. Dans le précédent exemple le time stamp devait être le bon à chaque visualisation et ne pouvait donc fonctionner avec un formulaire en cache.

De même la méthode est suffisamment transparent pour ne plus l'appliquer à seulement certains formulaires. Plus la peine donc de prévoir une page de "settings" pour définir qui est protégé de qui ne l'est pas. Par défaut tout formulaire accédé anonymement est protégé.

Comme pour la précédent méthode, la première chose à faire est d'ajouter un champ caché qui cette fois aura pour nom une valeur définie par une define php. Je vous conseille de choisir votre propre non de sorte à rendre le système le plus personnalisé possible.

Pour commencer ce nouveau module, reprenez l'ancien, enlevez la fonction de hook sur vilains_spammeurs_settings et modifiez la fonction hook vilains_spammeurs_form_alter comme suit :

  1. define('COOKIE_NAME', 'biscuit');
  2. define('COOKIE_VALUE', 'magique');
  3. function vilains_spammeurs_form_alter($form_id, & $form)
  4. {
  5.   global $user;
  6.   if ($user->uid == 0)
  7.   {
  8.     $timeStamp = time();
  9.     error_log('SpamKiller - ALTER - ' . $_GET['q'] . '/' . $form_id . '  = ' . COOKIE_NAME . '=' . $timeStamp);
  10.     $form[COOKIE_NAME] = array (
  11.       '#type' => 'hidden',
  12.       '#default_value' => $timeStamp
  13.     );
  14.     $form['#submit'] = array_merge(array (
  15.       'vilains_spammeurs_validator' => array ()
  16.     ), $form['#submit']);
  17.     return;
  18.   }
  19. }

Comme vous le voyez, il n'y a pas grande différence avec la précédent procédure mise à par le nom du champ et la suppression de la ligne désactivant la mise en cache.

Ensuite, nous allons introduire deux nouvelles fonctions pour modifier à la volée les champs textarea et textfield et y insérer un événement javascript :

  1. function phptemplate_textarea($element)
  2. {
  3.   $result = theme_textarea($element);
  4.         global $user;
  5.   if ($user->uid==0)
  6.     $result = str_replace("<textarea", '<textarea onkeyup="this.form.' . COOKIE_NAME . '.value=\'' . COOKIE_VALUE . '\';" ',$result);
  7.   return $result;
  8. }
  9.  
  10. function phptemplate_textarea($element)
  11. {
  12.   $result = theme_textarea($element);
  13.         global $user;
  14.   if ($user->uid==0)
  15.     $result = str_replace("<input", '<input onkeyup="this.form.' . COOKIE_NAME . '.value=\'' . COOKIE_VALUE . '\';" ',$result);
  16.   return $result;
  17. }

La seule astuce ici consiste à utiliser les hooks assez peu connus phptemplate_xxx qui sont appelés pour skinner les éléments textarea et textfield. Heureusement pour nous le moteur de thème phptemplate ne les utilise pas. Leur rôle est d'effectuer la transformation standard (ne pas ré-inventer la roue) puis, si mode "anonyme", de rajouter un événement "onkeyup" qui va, lorsque déclenché, donner à notre champ caché sa vraie valeur. Ainsi l'appui d'une touche dans n'importe quel champ texte (commentaire, login, etc..) va valider.

Il ne nous reste plus maintenant qu'à vérifier cette valeur en retour de formulaire comme dans le billet précédent en modifiant la procédure de validation comme suit :

  1. function vilains_spammeurs_validator($form_id, $form_values)
  2. {
  3.   $cookieValue = $form_values[COOKIE_NAME];
  4.   $failed = $cookieValue != COOKIE_VALUE;
  5.   error_log('SpamKiller - SUBMIT - ' . ($failed ? 'SPAM' : 'OK') . ' - ' . $_GET['q'] . '/' . $form_id . ' : ' . COOKIE_NAME . ' = ' . $cookieValue);
  6.   if ($failed)
  7.   {
  8.     error_log('Spam Content : ' . $form_values['comment']);
  9.     drupal_set_header('HTTP/1.1 666 Forbiden to spammers.');
  10.     print "Ceci est sûrement un spam. Si c'est un erreur, veuillez contacter l'administrateur";
  11.     exit ();
  12.   }
  13.   return;
  14. }

Voilà, c'est tout. Plus de spam, jusqu'à ce qu'ils trouvent une parade, bien évidement Wink Mais il est à noter que les spammeurs ont un net handicape par rapport à nous, s'ils jouent sur une masse, nous nous pouvons créer des cas particuliers...

Pour ceux que cela intéresse, les sources sont disponible sur le dépôt subversion :

svn co http://artisan.karma-lab.net/software/subversion/spam_killer/tags/v0.2/spam_killer

Commentaires

pti-seb , le 28 October, 2007 - 21:25

Personnellement j'ai virer le système de control de temps qui n'est absolument pas efficace chez moi.
Je vais essayer celui-ci, il me semble pas mal. j'ai adapter un peu en mettant un hash md5 dans le champ invisible qui se rempli à la saisie de touche.

Ulhume, le 28 October, 2007 - 21:56

Tu dois avoir un beaucoup plus gros volume de spams que moi je pense, cela explique que tes 10% sont plus prise de tête que les miens. Mais avec ce système là, j'ai pour l'instant purement et simplement 0 spams depuis 2 jours (sauf quant je désactive pour tester Wink.

Dis moi ce que cela donne chez toi ça m'intéresse grandement.

NB: S'ils cassent celui-là, cela veut dire qu'ils automatisent des navigateurs via des robots, un peu comme ceux que j'utilise pour tester les interface web de manière automatique (tests de regression) en simulant les mouvements souris et clavier. Et si c'est le cas, c'est re-bonjour captchas car là je vois pas ce que l'on peut faire.

Pti-seb , le 28 October, 2007 - 22:42

J'ai toujours du spam ...

Ulhume, le 28 October, 2007 - 23:51

Tu as mis les mêmes traces que moi ? Tu peux affirmer que le cookie a pris la "bonne" valeur à la validation du formulaire ?

Ulhume, le 29 October, 2007 - 00:14

J'ai jeté un oeil sur ce que je pouvais voir de ton code, un petit détail me pose un problème, ton hash fait planter le moteur jscript de konqueror (et peut-être celui de safari aussi) car il n'est pas autorisé de faire démarrer un identifiant (et donc un nom de champ) par un chiffre.

Sinon, il me faudrait plus d'informations de ta part pour voir qu'il cela vient car il reste une possibilité d'est d'utiliser prototyp ou un équivalent (jsquery pour drupal) pour hooker les champs à la volée. As-tu une stat du nombre de spam qui passent sur spam bloqués ? Car j'ai beau me dire qu'ils sont très intelligents nos spammeurs, j'ai du mal à imaginer qu'ils aient pu pousser la finesse jusqu'à simuler les évents javascript. Et comme sur les 30 spams que j'ai eu depuis ce matin, aucun n'est passé, je n'ai que tes logs comme base de travai.

Pti-seb , le 29 October, 2007 - 13:04

J'ai modifier un peu le formulaire suites à tes conseils. Est-ce que cela plante toujours chez toi ?

Sinon je vais analyser les logs pour voir se qui si passe, mais je suis sur et certain, j'ai à faire avec sûrement l'un des robots spammeur le plus intelligent qui existe.

Ulhume, le 29 October, 2007 - 15:06

Nope, désolé, j'ai encore eu un access denied sur mon IP... Je ne suis pas un spammeur intelligent !! Smiling

Sinon pour tes logs tiens moi au courant, j'ai bien une dernière parade mais cela deviens chaud (utiliser un formulaire généré sur une url spéciale et intégrée en ajax chez le client).

Pti-seb , le 29 October, 2007 - 19:33

Mes logs ne sont pas trop exploitable, on voit beaucoup de POST fait par des machines différentes. J'abandonne également le javascript, trop contraignant pour certains navigateurs visiblement.

Ulhume, le 30 October, 2007 - 18:26

Tu as eu quels problèmes avec des navigateurs ? Ca m'étonnes d'autant plus que tu as déjà du script sur ton submit si je me souviens bien.

phrenq , le 18 December, 2007 - 17:25

Qu'est-ce qui se passe avec les clients dont le javascript est désactivé ? Insertion de commentaires impossible ?

Ulhume, le 24 December, 2007 - 17:43

Yep, c'est l'inconvenient, mais l'utilisateur anonyme est plus que prevenu !!

Dab, le 5 May, 2008 - 12:45

Sympa cet anti spam et surtout très simple à mettre en place Smiling
Les spammeurs n'utilisent toujours pas la fonction onkey ?
Je l'ai intégré à mon futur blog, par contre je ne teste même pas si l'utilisateur est authentifié, me semble pas que ce soit nécessaire Smiling

Poster un nouveau commentaire

Le contenu de ce champ est gardé secret et ne sera pas montré publiquement.
  • Allowed HTML tags: <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd>
  • To highlight piece of code, just surround them with <code type="language"> Your code &tl;/code>>. Language can be java,c++,bash,etc... Everything Geshi support.
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • Textual smileys will be replaced with graphical ones.
  • Les adresses de pages web et de messagerie électronique sont transformées en liens automatiquement.

Plus d'informations sur les options de formatage

Connexion utilisateur
Sommaire
Commentaires récents