Jeudi 27 Novembre 2008 à 05h20::AutresLeçon d'humilité
Pour commencer, une remarque concernant l'antispam décrit dans le billet précédent. Juste pour signaler que je constate dorénavant dans les statistiques une impressionnante chute du nombre de spam à la journée.
Alors qu'auparavant il était courant de trouver plus de 1000 spams à la journée, sur les trois derniers jours, je n'en retrouve en moyenne plus qu'une cinquantaine.
Deux possibilités à cela :
- soit les robots se sont détournés progressivement du site lié à des tentatives infructueuses pour déposer un spam
- soit des réseaux de spams sont tombés
A suivre... Pour vous donner un ordre d'idée, je mets en capture d'écran le tableau de bord créé pour le suivi de la classe antispam sur les 15 derniers jours. Prêtez seulement attention à la colonne "Taille" des fichiers de logs et vous aurez un bon aperçu de la diminution des spams reçus :

Tout à fait autre chose. Cette semaine, j'ai eu l'occasion d'assister à une conférence Microsoft sur l'atelier de développement Visual Studio, tenu par l'excellent Pascal Belaud.
Je vais me garder de toute conclusion hâtive. En revanche, j'ai pris un gros coup de massue derrière la tête. J'ai cru être arrivé à un certain niveau de connaissance sur la programmation web. Ben là je viens de tomber affreusement bas. Une leçon d'humilité et une grande remise en question. Et ce n'est pas qu'une question d'outil, il s'agit également de méthodologie. Pour tout vous dire, après cette conférence, j'ai eu l'impression d'appartenir à la préhistoire de la programmation.
Ah si vous aviez vu l'intégration des tests unitaires, la création de l'architecture objet via un designer, la démo sur LINK, la gestion des multithread, la mise à jour des dll, la personnalisation d'un projet via un fichier de config propre, le passage d'un code prévu pour une application win vers le web puis vers un mobile puis vers un pocket pc puis vers un tablet pc, le tout en mode online ou offline, l'intégration intuitive des services web, la complétion de code vraiment intelligente, la maintenance d'objet grâce à la refactorisation... vous comprendriez mon désarroi... Je sais que la présentation était "idyllique", mais quand même, ça en jette.
Tant que je n'aurai pas créé un projet web entier avec Visual Studio, je me garderai de tout avis tranché. La conférence tenue n'était pas vraiment orientée web, et nous n'avons eu qu'un petit aperçu des possibilités offertes sur ce thème là. L'essentiel était centré sur la création d'application.
Mais sur cette courte exposition, une tendance de .Net est indéniable : la volonté de donner les moyens au développeur de se concentrer sur la conception de l'application au niveau métier en supprimant pour eux une large partie des difficultés de programmation sur le web.
Voici pour moi la différence la plus flagrante vis à vis de PHP. Je continue à penser que, client riche mis à part, vous ne ferez rien de plus en .Net qu'en PHP. En revanche, là où PHP n'intervient qu'au niveau serveur, .Net agit côté serveur ET côté client.
Je n'en dis pas plus pour le moment. Je n'oublie pas qu'il s'agissait d'une conférence Microsoft, pour Microsoft, par Microsoft. Une intervention qualifiable de merchandising. Je n'oublie pas non plus que la solution Microsoft a un coût.
Ce que je souhaite dorénavant, c'est une démonstration de l'outil dans un environnement web uniquement. Avoir un retour d'un développeur ayant franchi le cap PHP vers .Net, car chaque langage a ses points forts et ses points faibles.
J'ai créé beaucoup de choses en mélangeant PHP, JS, CSS, XHTML. J'attends encore de voir si l'on peut arriver au même niveau de détail avec un environnement censé géré les trois derniers pans pour vous.
Je vais essayer d'analyser tout ça à froid dans ma petite cervelle, je me pose encore beaucoup de questions et je ne veux pas tomber dans un euphorisme immédiat sans avoir réalisé une étude approfondie.
Tout ça pour dire que j'ai reçu une belle leçon d'humilité ce jour là, celle du genre à voir se faufiler une petite larme sur la joue... Mais ça a du bon les remises en question, il ne faut pas en avoir peur.
In your face comme disent les jeunes (enfin je crois...).
Lien permanent |
|
Stumble It!
Dimanche 23 Novembre 2008 à 17h08::PHP | SécuritéAntispam : de la théorie à la pratique
Il y a un peu plus d'un mois, je proposais un article cernant les alternatives au système de CAPTCHA. Y était listé différents concepts sans aucun exemple de fonctionnement au niveau du code, Peu après la rédaction de ce billet, je constatais que le système de commentaire disponible sur le blog d'un ami proche était allègrement sujet au spam.
La pollution de son site était accablante, l'obligeant à supprimer régulièrement et manuellement les commentaires faisandés directement via son interface d'administration SQL au risque de dépasser le quota fixé par son hébergeur à 5Mo. Pour lui, une seule journée sujet au spam augmente sa base régulièrement de 1Mo de données parasites.
Impossible de laisser un ami dans le désarroi, un sujet d'étude excellent par rapport à mon précédent billet, et j'étais lancé dans la réalisation d'un composant « antispam » basé sur les théories décrites dans mon précédent article.
Le système de blog sur lequel j'ai du travailler m'étant inconnu, je fus forcé d'écrire un objet indépendant de tout système, ce qui finalement me permet de le rendre disponible en téléchargement.
Que vous utilisiez un moteur reconnu comme Wordpress ou Dotclear, que vous propulsiez votre propre système, l'objet dont je vais décrire le fonctionnement est susceptible de s'adapter à votre site.
Mais avant de rentrer dans le vif du sujet, voici quelques constatations vis à vis des spams étudiés.
Les statistiques
Je précisais dans l'article qu'il subsistait grossièrement deux types de robots :
- ceux qui attaquent directement l'url de soumission de formulaire sans passer par le site
- ceux qui passent par le site pour soumettre les données.
Cette information se confirme. En revanche, je me suis trompé dans leur quantification : il y a plus de spams issus de robots remplissant le formulaire sur le site et soumettant les données que de spams issus de robots qui attaquent directement l'URL de gestion des données.
Les envois de spams sont massifs. Le blog recevait en moyenne un spam par minute, mais il ne s'agit pas d'un envoi équilibré dans le temps. Sur une heure, le site se fait attaquer deux ou trois fois maximum, mais sur une seule seconde, le module de statistiques arrive à enregistrer jusqu'à quatre commentaires.
Et encore, ce résultat est certainement sous évalué. Le but étant tout de même de supprimer les commentaires de la base de données, les statistiques sur les spams interceptés sont stockés dans un fichier XML journalier.
Pour ce faire, j'utilise le module SimpleXML de PHP, une librairie permettant de créer des documents XML avec rapidité. Mais ce dernier tombe régulièrement, finissant par écrire des fichiers XML invalides. En fait, il rentre en conflit avec des ouvertures simultanées en lecture / écriture sur le fichier de logs. Il est donc fort possible que l'envoi sur une seconde soit encore plus massif que ce que les statistiques laissent présager.
Il est également impossible de déterminer une fréquence régulière d'envoi des spams. Sur une journée, il peut y avoir trois fois plus de spam à une heure A qu'à une heure B, le lendemain, les données seront différentes.
Sans connaître le fonctionnement intrinsèque des robots, il semble toutefois que ces derniers assaillent aléatoirement le site et ne sont pas conçus pour lancer des attaques programmées à heure fixe.
Concernant les informations fournies par le client, je confirme une fois de plus qu'elles sont tout à fait inutiles pour détecter un spam. Au niveau de l'analyse des adresses IP, celles-ci correspondent régulièrement à des sites existants, probablement des réseaux d'entreprises.
Rares sont celles qui n'aboutissent pas lorsque testées dans un navigateur, et encore plus rares sont celles qui sont associées à un nom de domaine. La majeure partie des adresses IP correspondent à une page par défaut d'un serveur web, à l'instar d'Apache et des désormais célèbres « It works! » ou « Powered By... ».
Il s'agit donc clairement d'une usurpation d'adresse IP. Inutile d'espérer en tirer quelque chose, tout au plus nous risquerions de bloquer des commentaires véritables, issus d'un internaute parvenu sur notre site par le réseau de son entreprise. L'information devient inexploitable.
Concernant les informations sur les navigateurs, il n'y a pas grand chose à en tirer. A plus de 95%, le robot transmet un entête sur l'utilisation d'un navigateur Internet Explorer. Jamais vu trace d'un quelconque Mozilla, Opera, Chrome ou autre.
Sur ces navigateurs IE, ils sont à plus de 90% estampillés d'une version 6, aucune trace d'un IE7, quelques traces de l'obsolète IE5. On distingue également l'utilisation de plugins (MRA, DigExt), mais après des recherches approfondies, ces derniers ne sont en rien liés à des fins de spam.
De temps à autre, je retrouve un entête « SmallProxy 3.4.x Free » dont voici la page estimée « officielle » : http://smallproxy.ru/
Je vous laisse juge pour voir de quoi il retourne. N'ayant pas pris l'option araméen deuxième langue lors de mes études, je n'ai pas su comprendre son but réel. Tout au plus les captures d'écran semblent indiquer qu'il s'agit d'un outil permettant de tracer les requêtes serveurs, voire de les manipuler.
Est-ce symptomatique d'une volonté de comprendre et pirater le système antispam mis en place? Je n'en sais fichtre rien et mes tentatives de recherche dans Google ne m'ont apportées aucun indice...
Enfin, rien à voir avec les statistiques, voici que miasmatech est présent dans certains spams. Regardez plutôt :
[URL=http://www.<b style="color:black;background-color:#ffff66">miasmatech</b>.net/scripts/accueil/permalink.php?post_id=23#comAnchor]adult movie previews[/URL]
Il s'agit bien de mon site et d'un lien valide, qui, à en croire le robot ayant récupéré l'information, sert à mettre en ligne des bandes annonces de films pornos... Je ne sais pas si je dois en rire ou en pleurer, surtout que le lien fait référence à un article sur la sécurité des applications web et les attaques XSS...
J'imagine que ce « référencement » est lié à l'article sur les alternatives au système de captcha, où, dans la toute dernière partie consacrée à l'analyse de contenu, je liste quelques mots clés fréquemment rencontrés dans les spams.
A l'origine, j'estimais que le but des spammeurs étaient mercantiles : rediriger vers des sites illégaux ou augmenter le pagerank des sites dans les moteurs de recherches. Après le référencement de mon propre site dans un commentaire de spam, je me dis qu'il existe également des buts de malveillance au plus bas niveau. C'est triste mais bon, passons outre...
Antispam : l'objet
L'objet antispam proposé est indépendant de tout système de gestion de contenu. Seules deux méthodes seront à connaître, auxquelles viendront se greffer quelques notions sur sa configuration.
Pré-requis
Au niveau des pré-requis techniques, votre site doit être compatible PHP5.
L'utilisation des sessions est obligatoire, mais ce ne devrait être une contrainte pour personne. Tout au plus devrez-vous vous assurer de la présence en début de script PHP de la fonction « session_start() ».
Accessoirement, j'utilise dans l'objet la librairie MCRYPT. L'activation de cette librairie est testée dans la classe. Si elle n'est pas activée, l'objet fonctionnera toujours, il n'est donc pas rédhibitoire de ne pas posséder cette extension.
Avec un hébergement mutualiste OVH, l'extension MCRYPT est présente, ce qui laisse à penser qu'il s'agit d'une librairie de base activée chez la plupart des autres hébergeurs.
Au niveau du fonctionnement, l'objet va travailler le formulaire des commentaires de votre blog ou forum de manière à atteindre les buts suivants :
- obliger les robots à passer par l'application,
- empêcher les robots de comprendre la structure du site et d'en sauvegarder le contexte,
- détecter du spam via des vérifications simples hors système de CAPTCHA.
Tu causes, tu causes, mais concrètement, il fait quoi ton objet??
J'y viens, j'y viens...
Avant de rentrer dans le détail, voici un formulaire HTML type de gestion des commentaires sur un blog, grandement dépouillé pour que nous puissions nous concentrer sur l'essentiel :
<html>
<body>
<form method="post" action="saveComment.php">
<input name="name" type="text" size="25" value="Nom*"/><br />
<input name="url" type="text" size="25" value="Site web"/><br />
<input name="email" type="text" size="25" value="E-mail"/><br />
<textarea name="message" id="d" cols="30" rows="5">Commentaire*[/textarea]<br/>
<input type="submit"/>
<input type="hidden" name="923de58c761ce46641dcaed28efac0e9" value="297" />
<input type="hidden" name="ba8d0fd887e07fdf49837df7094a7421" value="foo" />
</form>
</body>
</html>
Tandis que le code PHP fut modifié comme suit :
<?php
session_start();
// Inclusion de l'objet
require 'antispam/antispam.class.php';
// Juste pour simuler un système de template
$tpl = file_get_contents('template.html');
echo antispam::transform($tpl);
?>
Mouais... Et ça signifie?
Si on met parallèle le code html après l'application de la méthode « transform » vis à vis des concepts de protection de mon précédent billet, nous allons retrouver les techniques suivantes :
Tester la provenance des données envoyées
Tous les noms de variables en provenance du formulaire sont codées de manière unique et valable uniquement pour la session de l'utilisateur courant. Il est impossible d'obtenir deux fois un nom de variable identique, ce qui supprime définitivement tous les robots qui attaquaient votre script de gestion des données via des paramètres pré-enregistrés.
Plus simplement, tout robot ne passant pas par vitre site sera bloqué.
Le robot remplit tous les champs de formulaire
Un champ supplémentaire est créé dynamiquement dans le formulaire. Rapport à l'exemple précédent, ces deux lignes nous intéressent :
<style type="text/css">#ka26b50a58a616cd1d7413f7f15e6d396{display:none}</style>
<input type="text" name="87c044eae7ad2f8fededf3581642ca23" id="ka26b50a58a616cd1d7413f7f15e6d396"/>
Le champ est invisible pour l'utilisateur « humain » grâce à la CSS. Les robots qui auront la naïveté de le remplir seront détectés puis ignorés.
Bien sûr, pour bien faire, il aurait fallu délocaliser le style « display : none » en dehors du formulaire, dans une ressource CSS externe. Néanmoins, j'ai conçu l'objet pour être autonome et indépendant de toute configuration externe. Ceci étant dit, vous avez accès au code source pour adapter le code de l'objet à votre fonctionnement si le coeur vous en dit.
Le robot remplit un formulaire à une vitesse inégalée
L'objet antispam va tester le temps mis à la rédaction du formulaire. Par défaut, celui ci est estimé à 10 secondes. Tout formulaire en deçà du temps fixé sera ignoré. C'est le but du second champ ajouté à votre formulaire :
<input type="hidden" name="c94869fef919f963239247dccd1dc834" value="HQ9zTk6G6MksCg=="/>
Ce champ contient en fait le timestamp correspondant à l'heure d'arrivée de l'utilisateur sur votre site. Les habitués auront remarqué qu'il est encodé en base64. Peut-être pensez-vous alors qu'il s'agit d'une protection, comment dire... pourrie. Mais en réalité, le codage en base64 est nécessaire car la valeur se cachant derrière tout ceci n'est en rien le timestamp en clair mais la valeur cryptée grâce à la librairie MCRYPT et l'algorythme « rijndael ». Les données étant binaires, elles sont traduites en base64 pour rester compatible avec un affichage textuel.
Cette valeur est encodée pour que les robots et surtout leurs concepteurs ne puissent pas déduire quel filtre est appliqué : une valeur timestamp est trop aisément détectable.
Ah ben bravo, maintenant mon code javascript ne fonctionne plus!
Évidemment, si vous utilisiez les noms des champs dans votre formulaire pour exécuter du code JavaScript, vu que la classe modifie ces valeurs dynamiquement, le code associé ne fonctionne plus.
La solution est néanmoins très simple à mettre en place et consiste à remplacer les appels par nom de champs en privilégiant les identifiants.
Par exemple, si vous utilisiez ceci :
<input type="text" name="username" {...} />
<script type="text/javascript">
if (forms[0].username.value == '') {...}
</script>
Remaniez votre code comme suit :
<input type="text" name="56c8e464f04bef0324252ebc4a875044" id="unIdentifiantAbstraitDePreference" {...} />
<script type="text/javasctipt">
if (document.getElementById('unIdentifiantAbstraitDePreference').value == '') {...}
</script>
Pour reprendre l'exemple du formulaire de démo :
<html>
<head>
<script type="text/javascript">
function checkForm() {
if (document.getElementById('a').value == '') {
alert('Merci de remplir le champ [Nom]');
return false
}
return true;
}
</script>
</head>
<body>
<form method="post" action="saveComment.php" onsubmit="return checkForm()">
<input name="name" type="text" size="25" value="Nom*" id="a"/><br />
<input name="url" type="text" size="25" value="Site web"/><br />
<input name="email" type="text" size="25" value="E-mail"/><br />
<textarea name="message" id="d" cols="30" rows="5">Commentaire*[/textarea]<br/>
<input type="submit"/>
<input type="hidden" name="parent_id" value="297" />
<input type="hidden" name="parent_name" value="foo" />
</form>
</body>
</html>
Et il s'appelle Garcimore ton objet??? Comment reconnaît-il les variables à coder?
Il n'y a bien sûr rien de magique dans cet objet, un peu de configuration est nécessaire avant de l'utiliser, mais j'ose croire être parvenu à une configuration simple et intuitive.
L'objet a été conçu sous le pattern Singleton. Ce nom barbare signifie seulement que l'objet s'instancie de lui même et qu'il n'a besoin d'aucune autre ressource pour fonctionner.
De fait, tout le paramétrage est réalisée sous forme de constantes internes et externes à l'objet.
Pour les étapes de configuration, vous devrez éditer le fichier « antispam.class.php ».
L'appairage entre une variable de formulaire et l'objet se configure grâce à des constantes externes.
Si je reprends le source de mon template, les champs suivants sont utilisés :
- name="name" : le pseudo de l'utilisateur
- name="url" : l'url vers son propre site
- name="email" : son adresse email
- name="message" : le commentaire
- name="parent_id" : un premier identifiant base de données
- name="parent_name" : un second identifiant base de données
Éditez le code source de la classe. Les premières lignes de code, hors commentaires, sont les suivantes :
define('CST_COMMENT_NAME' , 'name');
define('CST_COMMENT_URL' , 'url');
define('CST_COMMENT_EMAIL' , 'email');
define('CST_COMMENT_MESSAGE' , 'message');
define('CST_COMMENT_PARENT_ID' , 'parent_id');
define('CST_COMMENT_PARENT_NAME', 'parent_name');
C'est ici que se joue l'appairage des données. Tous les champs du formulaire devant être encodées sont déclarés sous forme de constantes. Vous pouvez en ajoutez ou en supprimer à votre guise.
Pour être dynamique, l'objet analyse les constantes définies et utilisent celles commençant par « CST_COMMENT_ ». Si vous devez ajouter un champ « langue » par exemple, ajoutez une constante comme suit :
define('CST_COMMENT_LANGAGE', 'langage');
Une note au sujet de la définition des constantes : il est très important de saisir uniquement les champs réellement présent dans le formulaire, leur existence étant testée dans la phase de validation des données.
Ça ne m'explique pas comment tu fais pour placer au bon endroit tes champs personnalisés...
Tout est basé sur le même principe : la classe ajoute ses propres champs en analysant votre code HTML et en partant à la recherche d'une chaine qui par défaut doit être « <!--ANTISPAM_FORM_COMPLETION--> ». Pour vous aider à la mise en place de l'objet, une erreur explicite est générée si la chaîne est introuvable.
Vous devez donc ajouter cette ligne entre les balises de votre formulaire. Peut importe sa place ensuite. Dans le cadre de la démo :
<form method="post" action="saveComment.php" onsubmit="return checkForm()">
<!--ANTISPAM_FORM_COMPLETION-->
<input name="name" type="text" size="25" value="Nom*" id="a"/><br />
{...}
</form>
Vous pouvez modifiez la valeur de cette chaîne en modifiant la valeur de la constante de classe nommée « CST_ANTISPAM_FORM_COMPLETION ».
Et sinon, d'autres configurations sont à disposition ?
Très peu. Dans l'absolu, vous en savez déjà assez sur les options de l'objet. Pour les curieux, regardons tout de même.
Comme je l'expliquais plus haut, l'objet utilise les sessions pour stocker les valeurs codées côté serveur. Par exemple, si j'affiche ma variable $_SESSION après application de la méthode « transform », j'aboutis au résultat suivant :
Array
(
[ANTISPAM] => Array
(
[IV] => èÚ¿ÂOÓû6`E}Ì´Ä`ÝX!'0S«-ý¹ï
[name] => 8c90060e90f60789a0a2ba5441b23860
[url] => a7ab23cadc3013be9d782607b3af13c2
[email] => 1aa4a8c40eb3495c78509bdb6fd88bb8
[message] => 0d1a0a6e2940f50403050debcfbd0725
[parent_id] => 1dfc79d0627b7c36bb7679aa0888306d
[parent_name] => 25a040b4ecbead67057d7d278c804493
[comments] => 26c573fdc3dce9bf7e752427fd238b40
[x0vb] => 1f40aba7802a4cf8492ff763b1fcc7cb
)
)
La classe centralise toutes les données dont elle a besoin dans une variable de type tableau « ANTISPAM ».
Je doute fort que vous ayez déjà un tel nom de variable dans votre tableau $_SESSION. Néanmoins, si tel était le cas, nous aurions des problèmes de conflit entre votre système et l'objet antispam.
Pour les résoudre, vous pouvez modifier le nom d'index du tableau grâce à la constante CST_ANTISPAM_SESSION_VAR :
const CST_ANTISPAM_SESSION_VAR = 'UN_AUTRE_NOM_UNIQUE';
Une autre constante est plutôt importante. Lorsque vous regardez le tableau de session, un index « comments » est déclarée. Il s'agit du champ caché à l'utilisateur qu'un robot est à même de remplir. Si le nom de votre champ de commentaire est identique, modifiez la constante CST_COMMENT_INPUT_IGNORE :
const CST_COMMENT_INPUT_IGNORE = 'votre_avis';
Ensuite, des options plus « ludiques » sont disponibles. Vous pouvez par exemple modifier le temps minimum requis par un humain pour remplir le formulaire. Il est fixé par défaut à 10 secondes. Si cette valeur ne vous paraît pas pertinente, modifiez la constante CST_ANTISPAM_MIN_WRITE_TIME :
const CST_ANTISPAM_MIN_WRITE_TIME = 30;
Une autre propriété dont je n'ai pas encore parlé est disponible. L'objet antispam peut également détecter un robot en comptant le nombre d'URLs présentes. Voici un exemple réel de commentaire fourni en URLs :
Great site. Keep doing.
<a href=\"http://web.tickle.com/member/XANAX_15\">xanax</a>
[URL=http://web.tickle.com/member/XANAX_15]xanax[/URL]
http://web.tickle.com/member/XANAX_15
<a href=\"http://web.tickle.com/member/LEVITRA_15\">buy levitra</a>
Un tel spam sera automatiquement intercepté car l'objet considère par défaut qu'un corps de message avec plus de trois liens est du spam. Vous pouvez modifiez le nombre de lien maximum autorisé en ajustant la constante CST_ANTISPAM_URL_FILTER :
const CST_ANTISPAM_URL_FILTER = 3;
En revanche, la classe a besoin de savoir quel est le champ contenant les commentaires. Elle utilisera pour ce faire la constante externe « CST_COMMENT_MESSAGE ». Si la constante n'existe pas, le test ne sera tout simplement pas effectué. Pour désactiver complètement ce test, positionnez la valeur à zéro.
Maintenant que tu as changé les variables de mon formulaire, comment je fais pour sauvegarder mes données???
Question légitime, mais si la classe est capable d'encoder le formulaire, l'inverse est possible. Voici à quoi va correspondre le tableau $_POST après validation des données :
Array
(
[9df6183ad7edc322c5f80479be49a5d6] =>
[72ea9e7f89e708c7881f7d6c4511efe2] => ON/EL0vfM4t5Sw==
[73c27ae7e51a68e0a31876879b4d9a82] => Olivier
[39b3760316de4e86e37fad856f7308e7] => http://www.miasmatech.net
[df0c58429492260cc2558140d298de94] => f.foo@foo.fr
[223a834dc7d429d1e9b6493b90d20001] => Lorem ipsum dolor...
[b6ef770dbf54894b91746aeea83e3c88] => 297
[651ac69e2a9a39910b47b40d427f8f40] => foo
)
Avant de traiter les données, vous devrez utiliser la méthode « isFormValid() ». Cette dernière a un double objectif :
- traduire le tableau $_POST conformément à ce que votre code de gestion attend
- détecter un spam
Cette méthode retourne un booléen. S'il vaut « true », vous pouvez procéder à l'enregistrement du commentaire dans votre base. S'il vaut « false », n'enregistrez pas le commentaire, mais laissez le comportement par défaut de votre code, généralement une redirection :
<?php
session_start();
if (!antispam::formIsValid()) {
// Il s'agit d'un spam. L'ignorer
header('Location: index.php');
exit;
}
// Tout est ok, poursuite des traitements
$sql = 'INSERT INTO...';
header('Location: index.php');
exit;
?>
Votre formulaire une fois passé à la moulinette de la méthode « formIsValid() » redevient conforme à votre script de traitement :
Array
(
[name] => Olivier
[url] => http;//www.miasmatech.net
[email] => foo@foo.com
[message] => Lorem ipsum dolor...
[parent_id] => 297
[parent_name] => foo
[comments] =>
[x0vb] => 1227429840
)
Je suis désolé, je ne te fais toujours pas confiance...
Et c'est bien normal... Après tout, j'arrive comme ça à l'improviste, je m'incruste, je modifie votre code, j'ignore des commentaires... Bref, je mets ma purée dans votre code, et vous souhaitez être sûr que la classe est fiable.
Parlons donc des fichiers journaux. Avant vous, j'ai bien sûr voulu être sûr du fonctionnement de l'objet. Chaque spam détecté par la classe peut donc être enregistré dans un fichier au format XML. Tout ce que vous avez à faire au niveau de l'objet est de positionner la constante CST_ANTISPAM_LOG_IGNORE_COMMENT à la valeur « true » :
const CST_ANTISPAM_LOG_IGNORE_COMMENT = true;
Un fichier log par jour sera généré. Il sera situé à la racine de la classe antispam et portera un nom conforme au format « antispamYYYYMMDD.xml ».
Le contenu est structuré comme suit :
<root>
<entree heure="09:25:08">
<controle code_erreur="1">Le formulaire transmis ne contient pas tous les champs requis (nb. champs fournis < nb. champ attendu).</controle>
<formulaire>
<string_name>auto owners insurance</string_name>
<string_url>http://www.findautoownersinsurance.com</string_url>
<string_email>d66p_test576@hotmail.com</string_email>
<string_message>Great work!</string_message>
<string_parent_id>202</string_parent_id>
<string_parent_name>20081022081129_weddinghand.jpg</string_parent_name>
</formulaire>
<serveur>
<http_user_agent>Mozilla/4.0 (compatible; MSIE 5.0; Windows 98; DigExt; MRA 4.0 (build 00768))</http_user_agent>
<http_referer>http://localhost/antispam_demo/index.php</http_referer>
<remote_addr>66.90.101.117</remote_addr>
</serveur>
</entree>
</root>
Vous serez alors en mesure de vérifier par vous même qu'il n'y a pas eu de mauvaises interprétations, voire vous créer votre propre module de statistique pour analyser la fréquence des spams, sur quelle contrôle ils ont échoués...
Avant de le mettre en place sur mon site, j'aimerai simuler du spam en local sur ma machine
Pour tester les différentes détection de spam, vous pouvez :
- recharger la page (F5) et vous dépêcher de cliquer sur le bouton de soumission pour envoyer le formulaire sous la barre des 10 secondes
- désactiver les CSS pour faire apparaître le champ caché et y saisir une valeur (cf. le module WebDevelopper pour Firefox)
- mettre plus de trois liens dans le corps du message
On pourrait aller plus loin en simulant concrètement un robot, mais bon, à un moment,...
Ok, tu m'as convaincu, je l'adopte. Mais est-ce vraiment fiable à 100%?
A 100%, certainement pas! Si la NASA se fait pirater, vous vous doutez bien que ce n'est pas mon petit système qui bloquera ces petits (cons) génies.
Il reste tout à fait possible de créer un robot qui outrepassera toutes les règles. Il lui faudrait néanmoins passer par le formulaire, remplir tous les champs dans un ordre spécifique (le nom de vos champs change, pas leur ordre d'apparition), attendre une dizaine de secondes puis lancer la validation du formulaire.
Mais je tiens le pari (sans rien gager tout de même, principe de précaution oblige) : la
classe suffira à 99,99% d'entre vous. Pourquoi en suis-je convaincu?
Tout d'abord, l'objet est destiné à des blogs ou des forums. Croyez-moi, si vous n'êtes pas une superstar du web, ceux qui vous attaquent passeront vite leur chemin pour trouver un site plus vulnérable. Sans vouloir vous faire offense, vous ne représentez pas assez de trafic pour qu'ils perdent un temps précieux à modifier leur robot en adéquation avec le système antispam.
Ensuite, n'oubliez pas qu'un robot cherche à faire des envois massifs. Si jamais il trouve la protection sur le timestamp, je doute malgré tout qu'il cherche à temporiser ses envois toutes les dix secondes. Je me trompe peut être, il ne faut jamais dire jamais, mais je n'y crois pour le moment tout simplement pas.
Enfin, il reste ce test sur le nombre d'urls fournies. Là encore, les robots se détourneront de votre site pour en attaquer un qui permettra de mettre plus de trois liens dans un commentaire. Ou alors ils sont vraiment féroces...
Si vraiment la classe ne suffit pas, il serait tout à fait légitime de se tourner vers les systèmes de CAPTCHA.
Tu as écris ton système pour être indépendant. J'utilise un système de blog, puis-je l'adapter?
Rien ne pourrait me faire plus plaisir que de voir cet objet intégré en tant que plugin dans un système tiers. Le code source est amplement documenté, vous pouvez le modifier, le distribuer, en faire ce que vous voulez, il est à disposition de la communauté.
Comme d'habitude, la seule chose que je demande est de citer miasmatech dans le source modifié. C'est tout. Vous pouvez mettre un lien de votre site vers le mien si vous le voulez, mais c'est loin d'être une obligation.
En guise de conclusion
Au jour de rédaction de cet article, le système antispam est en place depuis plus de deux semaines sur trois sites différents. Depuis, tous les spams ont été interceptés tandis qu'aucun commentaire « humain » ne fut supprimé par inadvertance. J'ai confiance dans cet objet. Néanmoins, je n'en oublie pas pour autant que je n'ai pas encore assez de recul.
Trop peu de temps s'est écoulé depuis sa mise en place, et les robots ne cessent d'évoluer. Si vous décidez d'utiliser cet objet et que vous avez des retours d'expérience à me faire parvenir, n'hésitez pas à laisser un petit commentaire.
Pour en terminer, voici le lien pour le téléchargement, démo comprise : antispam.zip
Lien permanent |
|
Stumble It!
Dimanche 26 Octobre 2008 à 16h25::AutresDéfis à l'horizon
Un nouveau projet. Comme d'habitude, j'en profite pour remettre en cause mes développements précédents en cherchant à simplifier et optimiser les processus de création, de maintenance et de conception d'interfaces.
Au bénéfice des années, je pense être parvenu à une ossature applicative valable aussi bien pour de petits sites que de vastes projets, bien que le résultat soit encore améliorable sur de nombreux points.
Il m'est encore impossible d'entrevoir un modèle pleinement satisfaisant, de type MVC, n'ayant pas le temps d'approfondir les différents designs patterns - « patrons de conception », je préfère l'usage de l'anglicisme - nécessaires à de telles réalisations. Mes lacunes en terme de programmation orientée tout objet sont largement perceptibles.
Les premiers composants auxquels je me suis attelé relevaient de la brique logicielle, de l'outil. A ce titre, ils sont tous autonomes mais mériteraient de s'intégrer dans un schéma global, autrement dit un framework (ensemble de bibliothèques modulaires).
A force d'être confronté à des problématiques toujours plus ardues, je fus naturellement amené à créer d'autres types de classes, relevant de la logique métier. Cela m'a permis de structurer de manière simple des opérations complexes. Le travail s'en trouve grandement facilité, tant dans sa réalisation que dans sa compréhension, en scindant de manière logique, ordonnée et réutilisable des portions de codes autrement plus désagréables si codées en séquentiel.
Dorénavant, j'arrive à concevoir l'imbrication des objets métiers de base. Là encore, les avantages sont multiples, notamment pour le développement en équipe. Les programmeurs se cantonnent à coder le déroulement des opérations en s'appuyant sur des outils métiers premiers préalablement modélisés.
Lorsque je regarde le noyau de mes applications actuelles, je me rends compte que sa structure se rapproche de plus en plus du modèle trois tiers, sans en être réellement un. Les modèles sont mes objets métiers, les vues sont représentées par un objet « template » (outil que je souhaite prochainement abandonner) alimenté par des objets relevant de la logique applicative, à l'instar d'un contrôleur. Mais une large partie à mon goût est encore gérée en programmation non-structurée, notamment pour l'imbrication des différentes couches. Pour aboutir à une conception tout objet, je manque encore de repères tant dans les design patterns que dans les structures à créer et implémenter. La conception « modèle - vue - contrôleur » est clairement la prochaine étape.
Certes les designs patterns « Singleton » et « Factory », ainsi que l'utilisation de quelques itérateurs de la SPL sont peu ou prou maîtrisés. A la lecture de certains articles, je me suis rendu compte que je connaissais également le polymorphisme, mais plutôt à mon insu (bon ok, quand ça relève du hasard il est délicat prétendre connaître...).
Reste à creuser d'autres notions importantes comme les patterns « proxy » et « observer », à gérer plus efficacement la levée et la gestion des exceptions, et plus globalement décoder « MVC » et sa structure sous-jacente. Trop de zones d'ombres subsistent, m'empêchant de tenter l'aventure « full OOP ».
J'ai donc conscience de mes lacunes, des orientations et des décisions à prendre pour la création d'applications plus simples à maintenir (OOP), à déboguer (tests unitaires) et déployer (autre sujet sur lequel je compte bien m'investir et sur lequel je reviendrai très certainement).
Malheureusement, comme pour beaucoup d'entre nous, le temps ne joue pas en ma faveur et se lancer aveuglément dans une structure dont la maîtrise complète n'est pas avérée serait gravissime. Hors de question d'avancer à tâtons ou de commettre des erreurs par manque de recul sur le sujet. Comme dans chaque entreprise de moyenne structure, un nouveau projet est souvent en retard avant le début même de sa conception. Impossible dans de telles conditions d'investir son temps dans de la veille technologique, l'analyse puis la réalisation priment.
Et justement, nous devons mettre sur pied un nouveau projet. Lors de récentes conversations consacrées à une étude préliminaire sur les choix techniques à mettre en oeuvre, nous avons abordé le thème de l'architecture objet, et avons évoqué superficiellement la modélisation des données en s'appuyant sur une formalisation UML. J'avoue immédiatement mes carences en connaissances UML - autant dire nulles -, ce qui ne m'empêche pas de connaître son existence et son utilité.
Rapidement, UML a été abandonné. Nous en resterons à la modélisation Merise, puisqu'il s'agit de ce que nous savons faire de mieux, tout en essayant lors de la conception du MCD d'anticiper l'architecture objet, dont le choix fut validé.
Bien que ce projet soit motivant, ne serait-ce que par les ambitions avouées, je ne peux m'empêcher de me poser nombre de question sur sa faisabilité. Mon scepticisme légendaire n'y est sans doute pas pour rien dans ce sentiment...
Il est la déclinaison d'une application client-serveur vieillissante - ou en fin de vie, ce qui permet d'enlever la connotation péjorative - vers une version full web mieux structurée car prenant acte des erreurs passées et des contraintes nouvelles. Outre la formation nécessaire pour la majorité des membres de l'équipe vers une technologie dont ils n'ont que de vagues connaissances, l'environnement de développement reste à définir. A la lumière de ces deux éléments, je trouve qu'il y a vraiment de quoi angoisser.
Même si je suis un fervent défenseur de PHP, je ne suis pas sûr que ce dernier soit à la hauteur des objectifs fixés. J'adore ce langage, cela fait des années que je le pratique, je le trouve à la fois puissant et souple, et je suis persuadé de sa pérennité et de sa solidité (love me, pleeease looove meeee....).
Néanmoins, comment migrer une équipe habituée à coder dans un environnement de développement comprenant à la fois un compilateur et un IDE pour la conception des interfaces vers un environnement où il n'y a ni compilation ni IDE? Je sais par avance que pour eux, un passage éventuel à PHP serait vraiment difficile et perturbant. On ne peut décemment pas les en blâmer tant les différences entre les deux mondes sont immenses. Même le jour et la nuit ont plus de points communs...
Autre souci de taille, PHP s'interface très difficilement avec les outils bureautiques. Alors, bien que nous ayons évoqué le sujet des clients riches (rien à voir avec la crise financière ou les parachutes dorés), nous réfléchissons à une migration Visual Studio et son framework « .Net ». Au passage, j'attends encore la preuve que cet outil permettra de résoudre les problèmes liés à la bureautique, notamment concernant les fusions (passage des données à un document stocké sur le poste du client et non sur le serveur). Nous sous entendons pour le moment qu'il s'agit là d'un produit Microsoft, et qu'il a à fortiori plus de chances d'intégrer une réponse que les autres langages.
Cet atelier est-il véritablement la solution? J'en doute. Changer radicalement d'outil de développement, c'est également s'ouvrir à une multitude de nouveaux problèmes, dont le principal reste son apprentissage jusqu'à son contrôle. Il a été mesuré - en interne, donc ne s'appliquant qu'à notre structure - qu'un portage d'un module complexe client-serveur Delphi vers le web nous procure une perte de temps d'environ 60% (PHP - Javascript - XHTML - CSS 2 - SQL Server et Reporting Services).
Mais ce temps de développement supplémentaire est surtout - voire intégralement - lié à l'environnement client et non à l'environnement serveur. Je ne crois pas, vis à vis de mon expérience dans le métier et des problèmes que nous aurons à résoudre, que les outils du framework « .Net » nous permettrons de réduire ces temps, à moins que nos objectifs ne soient revus à la baisse. Cela reviendrait à se limiter aux composants de base du framework et réviser nos interfaces pour qu'elles collent à ce que l'outil est capable d'offrir nativement. Mais même en revoyant les fonctionnalités à la baisse, je reste convaincu que ces pertes de temps seront encore supérieures à nos productions actuelles durant toute la phase de formation à l'outil.
Mon avis sur le sujet est clair : pour que ces pertes de productivité décroissent, il n'y a que deux solutions : soit utiliser un client riche, soit utiliser un navigateur unique. Je ne reviens pas sur la première proposition, abandonnée suite à des arguments sans intérêts à décrire ici mais tout à fait recevables. Quant à la seconde, elle est prohibée eu égard à la cible visée. Pour autant, d'un avis personnel, toute autre solution que les deux précitées engendrera inévitablement des temps de réalisation plus long.
Je suppose que le scénario « Visual Studio » sera approuvé. Hélas, je doute fort de nos capacités à maitriser à la fois un nouvel atelier de développement tout en se formant simultanément aux architectures orientées objet. J'appréhende même l'effet pervers inverse consistant à utiliser une couche OOP (le framework .Net) pour coder au final en séquentiel - même avec une programmation évènementielle, je considère qu'un code est séquentiel dès lors qu'il n'est pas organisé en composants interagissant les uns avec les autres -.
Trop de nouvelles technologies à apprendre, manque de temps, compétences en deçà des pré-requis établis, et parallèlement, un virage technologique qui doit avec certitude être entrepris. Une évidence s'impose. Nous serons toujours en retard dans le milieu du développement, nous ferons toujours des erreurs de conception. Combien de fois ai-je pesté après la découverte de nouvelles astuces impossibles à insérer dans un projet débuté depuis bien trop longtemps? Ce milieu évolue tellement rapidement... Est-ce une raison pour se décourager?
Lorsque je compare le code de certaines applications libres avec le nôtre, ou si je compare directement nos applications avec les travaux concurrents, il est évident qu'à société à potentiel identique, nous n'avons pas à rougir de nos productions. Et plus important encore, même si le résultat final n'est pas à la hauteur des ambitions techniques que nous aimerions porter, la satisfaction du client rapport à l'application fournie est incontestable.
Alors certes, je jalouse furieusement les développements de Google, notamment « Google Doc » puisqu'il gère ce qui est pour nous une contrainte technologique quasi insurmontable, mais nous ne disposons pas des mêmes moyens. Arrêtons de rêver et compensons nos lacunes actuelles en fructifiant l'expérience acquise dans notre spécialisation. Portons une attention toute particulière sur l'ergonomie, l'accessibilité, la facilité d'utilisation et la réponse aux exigences métiers de notre audience.
Car soyons honnête, l'utilisateur final - hors DSI, ces entités qui ne jurent que par la lecture du 01 Informatique et parfois détenteur de la clé d'une vente - se fout royalement de savoir si l'application est OOP, tant que le logiciel fourni correspond à ses besoins et lui facilite la gestion de ses tâches quotidiennes. La qualité finale d'un produit ne se juge pas sur ses seuls préceptes techniques, mais également sur ses fonctionnalités et sur la capacité de ses concepteurs à le maintenir et le faire évoluer.
L'important, d'un point de vue accomplissement personnel, est de progresser continuellement, de savoir remettre en cause ses connaissances, d'admettre ses erreurs, tenter d'y pallier et aller de l'avant.
Donc non, il n'y a pas lieu de se décourager (si si, la question était posée quelques lignes plus haut), mais on ne peut définitivement pas être à la pointe des technologies sur le long terme.
Lorsque je suis arrivé dans ce milieu, l'utilisation des services Web, du format XML, de la structure MVC (quand je pense que certains me parlent de SOA, les larmes de l'ignorance me montent aux yeux...) était bien loin d'être généralisée.
Le domaine du web demande également de multiples aptitudes dans des langages/techniques forts différents. Est-il raisonnable d'envisager la connaissance parfaite de JavaScript, PHP (ou .Net, ou Ruby, ou J2EE ou Python ou tout ce que vous voudrez...), Merise, UML, CSS, REST, SOAP, SQL, XForm, XML, XSLT (...), sans parler des connaissances hors programmation comme le paramétrage serveur, la gestion des montées en charge, les connaissances réseaux ou encore les protocoles HTTP, FTP, TELNET, DICT, LDAP, j'en passe et des meilleurs.
C'est un réel problème. Dans de grandes sociétés, les postes et les compétences de chacun sont clairement définis. Dans d'autres... il faut faire avec les moyens du bord. C'est un challenge perpétuel, et lorsque j'entends certains de mes collègues penser que nous ne sommes pas à la hauteur, je ne peux qu'exprimer mon désaccord : nous sommes surtout doués pour tenir la charge actuelle si l'on se replace avec justesse et esprit critique dans le contexte de notre effectif, certes grandissant mais toujours trop restreint.
A cela s'ajoute, dû à des prérogatives de clients importants, l'emploi de technologies dont nous n'avons pas la maîtrise initiale, ce qui conduit inévitablement à la réduction significative de nos capacités de réaction et de notre productivité. On aimerait tout gérer, tout connaître, satisfaire la moindre demande au prétexte de la sacrosainte satisfaction cliente... Au final, nous surestimons nos aptitudes et finissons par gérer des problèmes liés à de l'environnement, le tout en addition aux traditionnels soucis applicatifs, qui reste notre compétence première (pas les soucis, les applications bande de vilains).
Tout ou presque fût à apprendre « sur le tas ». C'est valable aujourd'hui, ce le sera encore demain. A nous de trouver le juste équilibre entre compétences internes, nécessité d'évoluer, besoins du client et ressources à disposition. La résolution de l'équation n'est pas simple, mais le défi diablement exaltant...
PS : il est hallucinant de constater à quel point il est aisé de dévier d'un sujet initial. Ce texte n'est en rien représentatif du sujet originellement prévu qui sera donc abordé une prochaine fois. J'ose espérer tout de même que la structure de ce billet est restée un minimum logique et cohérente...
Lien permanent |
|
Stumble It!