Les
spam
s sont une véritable infection, ce n'est un secret pour personne. Et les solutions pour lutter contre ce fléau sont aussi nombreuses que moyennement satisfaisantes. Il peut s'agir de déléguer ce travail à un service dédié (ex.
akismet
), de les traiter à la main (ne riez pas, je l'ai fait pendant un temps
) ou encore d'utiliser les fameux captchas, ces images illisibles qui sont d'une utilisation extrêmement désagréables pour les contributeurs.
Ce que je vous propose ici c'est une implémentation avec Drupal qui n'a rien de révolutionnaire tout en étant très efficace. L'idée étant de vérifier que les commentaires (et tous les formulaires en fait) sont en toute probabilité écrits par un humain.
Le spammeur va vite, très vite même. La plupart du temps il travaille en deux temps. Un premier de reconnaissance analyse vos pages. Soit il cherche à savoir quel plate-forme vous utilisez (drupal, wordpress, etc.) soit il analyse en aveugle les formulaires à la recherche de celui des commentaires. A ce titre il travaille un peu comme GoogleBot. Au passage, dans la série des légendes, contrairement à Google les scripts ne l'arrêtent pas. En tout cas ne les arrêtes pas tous, loin de là. Cacher un formulaire par un script ne fonctionne juste pas sauf à le mettre dans un fichier séparé, solution difficile à mettre en oeuvre sur la forme d'un plug-in Drupal. J'en ai même trouvé un qui n'était pas arrêté par les "boites de dialogues" javascript, les simulant allègrement...
Ensuite, une fois que votre site a été proprement analysé, il est mis en base de donnée. A partir de là, votre profile est utilisé à l'infini pour expédier des contenus de formulaire sous la forme d'une requête POST forgée proprement. Si la requête ne marche plus (formulaire modifié par exemple), le spammeur (ou plutôt son robot) va ré-analyse votre site et recommencer son manège.
Finalement la seule chose qui bloquerait proprement notre spammeur c'est le temps. Car soit il analyse (requête GET) et poste un commentaire (requête POST), soit il a déjà analysé et mémorisé le formulaire et ne fait plus que poster. Dans les deux cas, vu de votre serveur, une personne soumet un formulaire (POST) soit dans un temps très long, soit très court après un GET. L'idée de notre anti-spam maison est donc de stocker dans le formulaire une valeur de temps en secondes. Puis lors du post de comparer cette valeur avec celle de l'heure courante. Si le temps est inférieur à 10 secondes, c'est sûrement un spam (ou un utilisateur qui tape plus vite que son ombre
).
Pour implémenter cette fonction dans Drupal, il va nous falloir créer un module spécifique. Il y a déjà pas mal de littérature sur ce sujet je ne vais donc pas trop m'étendre là dessus. Un module drupal est un simple dossier portant le nom du module, ici vilains_spammeurs. Ce dossier contient deux fichiers, l'un d'information nommé vilains_spammeurs.info qui contiendra tout simplement :
Et au même endroit, un fichier vilains_spammeurs.module qui contiendra pour l'instant
Ceci fait, il suffit d'aller dans votre section administration/site building/modules pour activer ce simplissime plugin qui pour l'instant ne fait rien. Mais pas pour longtemps.
La première chose à faire est donc de stocker la date, ou plus exactement le nombre de secondes depuis 1970 dans un champ caché de tous les formulaires. En effet, tant qu'à faire, autant tout protéger dans la mesure où ne ciblons que les utilisateurs anonymes. Cela ne touche du coup que les commentaires et les créations de comptes.
Drupal fonctionne par "hook", c'est à dire que chaque module peut être étendu en fonctionnalité par d'autres modules sous réserve que ces derniers déclarent les bonnes méthodes. Par exemple, le module de "form", avant d'afficher un formulaire, va chercher dans tous les modules une méthode qui se nomme nomDuModule_alter_form. Cette méthode permet de modifier un formulaire. Donc si nous ajoutons à notre module une méthode vilains_spammeurs_alter_form, elle sera donc appelée en nous donnant l'opportunité de rajouter notre champ caché.
La méthode est assez simple. Tout d'abord on ne modifie que les formulaires des utilisateurs anonymes ($user->uid=0). On ne modifie pas non plus tous les formulaires. C'est le rôle du variable_get($form_id . '_spam_filtered') qui renvois un booléen en fonction de l'id du formulaire et que nous allons gérer un peu plus loin. Ensuite, petite astuce, on intercale notre propre méthode (vilains_spammeurs_validator) de validation dans la chaîne des validateurs de ce formulaire. Enfin, on ajoute un champ caché contenant le nombre de secondes depuis 1970 (fonction time() ). Autre astuce, le nom du champ est variable, il s'agit d'un HASH MD5 de l'adresse IP du client, rendant impossible le rejoue d'une requête mémorisée sur une autre machine que celle qui as affiché ce formulaire.
Deuxième étape, la validation du formulaire. Il va falloir ajouter à notre module la fonction vilains_spammeurs_validator que nous avons déclaré plus haut.
Là aussi rien de très compliqué. Comme cette procédure n'est appelée que si le formulaire a été modifié par la procédure précédente, et qu'il n'a été modifié que si l'utilisateur est anonyme, pas la peine de revérifier que nous somme dans le cas "anonyme". Son travail se borne donc juste à extraire le temps d'origine du formulaire, à recréer notre nom de champ magique à partir du MD5 de l'IP client et de faire une différence avec le temps courant. Le define juste avant la procédure définit le temps minimum pour considérer que nous avons affaire à un spam. Ce temps minimum est récupéré d'une variable d'environnement Drupal en fonction de l'ID du formulaire. Nous verrons plus loin le réglage. La variable $failed prends donc la valeur true si ce temps est en dessous de celui défini. Sa valeur est testée par la condition qui suit et qui affiche ainsi le message d'avertissement.
Pour archives, nous mettons d'abord dans les logs les caractéristiques du SPAM. Ensuite nous fabriquons une mini-page avec un code d'erreur bidon (ici 666) et un texte d'explication. Du coup, s'il s'agit d'une erreur (utilisateur trop véloce), il suffit de revenir en arrière et de revalider pour que le commentaire passe. Une fois le message affiché, nous faisons un très brutale appel à exit() qui romps ainsi la chaîne de traitement du formulaire et empêche sa sauvegarde. Fin de l'histoire.
Il manque cependant encore un détail, le paramétrage de notre modules. Il faut en effet pouvoir changer les valeurs XXX_spam_filtered et XXX_spam_delay. Pour cela, nous utiliserons un paramétrage simple, sans base de données :
Voilà, comme je le disais, c'est simple et efficace. Cela ne demande pas de javascript côté client et cela prend peu de ressources. Je n'ai pas de doute quant au fait que cette technique soit un jour contournée mais pour l'instant, cela bloque 100% de mes spams.
Pour ceux que cela intéresse, les sources sont disponibles ici sur le dépôt subversion : http://artisan.karma-lab.net/software/subversionspam_killer/tags/v0.1/spam_killer
- répondre
Ulhume, le 25 October, 2007 - 12:3850 spams tentés depuis l'écriture de cet article, il semble que la protection fonction
- répondre
Pti-seb , le 25 October, 2007 - 13:45J'ai tenté la mise en place d'un atni-spam fait maison, j'en suis arrivé à la conclusion suivante, les robots spammeurs ne font pas que envoyé un POST ou un GET, il sont capable d'ouvrir un navigateur, aller sur le site et poster un commentaire comme le ferait une vrai personne.
J'avais déjà vu un truc comme ça sous windows à une époque, on fesait une action à la souris, genre on clique sur une icône du bureau et cela génrère un script.
Si on rejoue le script, on voit la souris refaire la même chose toute seule.
Je vais implémenter ta méthode sous dotclear, je te redis si cela foncitonne. En tous les cas, si elle fonctionne, je ne peux m'empêcher de penser : pour combien de temps ?
- répondre
Pti-seb , le 25 October, 2007 - 13:50Au faite, pour consolider ta méthode, il faudrait mieux faire un hash md5 ou sha de la valeur du temps + ip de l'utilisateur + chaîne password. Sinon un robots spammeur peut simplement envoyer la valeur du temps qu'il souhaite pour déjouer le système.
- répondre
Ulhume, le 25 October, 2007 - 16:02Pour répondre à ton premier post, ça va bien dans le sens de ce que j'ai constaté. Les scripts sont bien joués comme le reste. Si effectivement il s'agit d'une automatisation d'un IE (ce qui est en tout cas souvent indiqué dans les signatures) comme tu l'indiques cela prend du sens. Au début j'avais tenté la méthode d'offuscation des formulaires et les remplaçant à la volé par un javascript à base de document.write et de liste de code UTF-8. Et les spams ont continué à affluer. J'ai donc abandonné cette méthode qui était pourtant élégante.
Pour ce qui est du moment où les bots de spam pour comprendre l'astuce, je me doute qu'il arrivera un jour ou l'autre. Mais pour y arriver, les deux seules solutions sont pour eux sont :
1/ S'il s'agit d'un vrai robot (pas d'un navigateur automatisé), de renvoyer le bon timestamp (genre ce que l'on insère + 10). Mais là cela leur demande de faire du code spécifique. Et on peu parer ce coup en mettant le nom du champ timestamp en settigs dans Drupal (ou dans ton CMS). Et en le générant aléatoirement à la première utilisation. Ainsi il va être difficile pour eux de faire une parade à la méthode en elle-même.
2/ S'il s'agit d'un "navigateur scripté" (genre un gecko ou un mshtml), il devra temporiser comme un humain leur réponse. C'est parfaitement imaginable en lançant un nombre N de GET à la suites, puis en reparcourant la liste depuis de début en faisant des POST. Mais l'inconvénient est qu'ils vont alors devoir lancer énormément d'instances en parallèle pour "remplir" ces 10 secondes. Sinon, cela va leur faire perdre du temps.
Ceci dit, pour l'instant, lorsque je regarde les logs, il semble que ce soit tous des 0s, c'est à dire des POST qui renvois la valeur timestamp à l'identique. Et si je regarde les logs je n'ai AUCUN GET au moment des faits. Pourtant il n'a pas pu inventer cette valeur. Là j'avoue que c'est un mystère pour moi.
La seule requête suffisamment proche dans le temps est un autre POST qui a aussi été débouté. Donc rien qui lui permette de récupérer les données du formulaire.
Maintenant le problème du hash c'est qu'il devient impossible de faire un delta pour évaluer le temps de réponse (ou alors j'ai mal compris). Et en plus le système est déconnecté en cas d'utilisateur authentifié (il n'y a plus de spam dans ce cas). En revanche, je retiens ton idée de l'IP car une chose est sure, depuis deux jours, je ne reçoit QUE des POST et plus de GET, si je compare IP à IP. Comme si l'enregistrement avait été ou est fait sur une machine donnée, et les POST sur d'autres. Donc encapsuler un hash de l'IP peut être un bon moyen aussi. A tester
- répondre
Pti-seb , le 25 October, 2007 - 16:10Bon ben j'ai mis en place la méthode, et j'ai toujours du spam à passer. Je laisse quand même le code car j'ai l'impression qu'il filtre un peu plus quand même (9 spams en 2heures).
Pour le hash md5 c'est pas possible, j'ai vue cela après, je suis donc partie sur une solution de cryptage/décryptage (cf. code source mes formulaire de post) qui fonctionne très bien.
PS : Sont coriace ces robots...
- répondre
Ulhume, le 25 October, 2007 - 16:28Hum.. Et qu'est-ce que tu as dans tes logs ? Quel est la différence de temps pour les spams qui passent ?
- répondre
Ulhume, le 25 October, 2007 - 16:44Bon, pour être sur d'être dans un cas normal, j'ai remis les url de commentaires aux normes drupal (comment/reply), comme cela je vais voir si j'ai moi aussi encore du spam qui passe.
- répondre
Ulhume, le 25 October, 2007 - 18:04Bon, j'ai compris le coup du 0s. En fait mon test de borne max ne servait à rien car l'utilisation du paramètre #default_value impliquait que même si le client n'avait pas posté ce champ, il était mis à la valeur par défaut de l'heure courante. Du coup pas de champs => time_stamp=heure_courante => 0s de temps de réponse => SPAM.
J'ai aussi repris l'idée de Pti-Seb pour intégrer l'IP client dans la méthode en l'utilisant, sous la forme d'un hash MD5 comme nom de champ. Du coup, si le spammeur utilise une autre IP et poste une requête pré-forgée, il tombe dans le cas de figure précédent.
Voilà, en espérant que cela serve.
- répondre
Bast , le 18 December, 2007 - 17:44L'adresse fournie pour le dépôt subversion ne mène nul part
http://artisan.karma-lab.net/subversion/drupal/modules/spam_killer/tags/...
- répondre
Ulhume, le 24 December, 2007 - 17:33Euh.. Tu peux reverifier car je viens de tester et j'arrive bien sur le contenu du module...
Poster un nouveau commentaire