<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Artisan Numérique</title>
  <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1508"/>
  <link rel="self" type="application/atom+xml" href="http://artisan.karma-lab.net/node/1508/atom/feed"/>
  <id>http://artisan.karma-lab.net/node/1508/atom/feed</id>
  <updated>2008-07-08T11:48:01+02:00</updated>
  <entry>
    <title>Drupal, cacher les adresses email</title>
    <link rel="alternate" type="text/html" href="http://artisan.karma-lab.net/node/1508" />
    <id>http://artisan.karma-lab.net/node/1508</id>
    <published>2008-03-12T21:15:10+01:00</published>
    <updated>2008-07-08T11:48:01+02:00</updated>
    <author>
      <name>Ulhume</name>
    </author>
    <category term="Drupal" />
    <category term="drupalfr.org" />
    <category term="Planet Libre" />
    <category term="Tutoriel" />
    <summary type="html"><![CDATA[<p>Dans un précédent article, j'abordais une manière d'éradiquer le "SPAM de formulaires". Et la technique marche si bien que je n'ai plus une seule saleté depuis. Mais récemment, dans un autre secteur, je me suis mis à recevoir des pouriels sur document.write(String.fromCharCode(60,97,32,104,114,101,102,61,39,109,97,105,108,116,111,58,97,114,116,105,115,97,110,64,107,97,114,109,97,45,108,97,98,46,110,101,116,39,62,97,114,116,105,115,97,110,64,107,97,114,109,97,45,108,97,98,46,110,101,116,60,47,97,62));. J'ai mis un certain à en capter la cause : à deux reprises cette adresse appairait, en clair, sur ce site. Du coup les spammeurs ont fini par tomber dessus et voguer galère... Du coup, je me suis attelé à la mise en oeuvre du très bon système de filtres de Drupal pour régler le problème à la source...</p>
    ]]></summary>
    <content type="html"><![CDATA[<p>Dans un précédent article, j'abordais une manière d'éradiquer le "SPAM de formulaires". Et la technique marche si bien que je n'ai plus une seule saleté depuis. Mais récemment, dans un autre secteur, je me suis mis à recevoir des pouriels sur <script>document.write(String.fromCharCode(60,97,32,104,114,101,102,61,39,109,97,105,108,116,111,58,97,114,116,105,115,97,110,64,107,97,114,109,97,45,108,97,98,46,110,101,116,39,62,97,114,116,105,115,97,110,64,107,97,114,109,97,45,108,97,98,46,110,101,116,60,47,97,62));</script>. J'ai mis un certain à en capter la cause : à deux reprises cette adresse appairait, en clair, sur ce site. Du coup les spammeurs ont fini par tomber dessus et voguer galère... Du coup, je me suis attelé à la mise en oeuvre du très bon système de filtres de Drupal pour régler le problème à la source...</p>
<p>Les filtres drupal</p>
<p>  Je ne vais pas m'amuser à comparer Drupal à ses voisins que je ne connais pas assez peu. Mais un aspect ultra-puissant de ce CMS est sa capacité à altérer par couche successives (filtres) un texte d'origine (billet ou commentaire) pour en arriver au résultat visible. On peut par exemple ajouter un filtre qui va permettre l'utilisation d'une syntaxe "WIKI" dans vos article, ou un filtre qui va développer des tags spéciaux pour par exemple insérer des images ou des vidéos, ou encore un filtre qui va transformer les smileys texte en image, etc...</p>
<p>  Dans le cas qui nous intéresse, nous allons créer un filtre qui va remplacer toutes les adresses courriel par une version seulement lisible par un humain. Et ce en partant du principe que, contrairement aux formulaires, les robots qui collectent les adresses mails font comme google, lisent le texte brut de la page et n'interprète pas le JavaScript. </p>
<p>Module de base</p>
<p>  Nous allons maintenant jeter les base de notre filtre. Comme toujours il est plus sain de créer le dossier du module dans drupal/site/all/mes_modules. Ici nous allons créer un dossier email_address_obfuscator. </p>
<p>  Dans ce dossier commençons par ajouter le fichier de description du module :</p>
<div class='code-container-area'>
<div class='code-container-caption'>fichier email_address_obfuscator.info</div>
<div class='code-container'>
<div class="code">
<ol>
<li class="li1">
<div class="de1">name &nbsp; &nbsp; &nbsp; &nbsp; <span class="sy0">=</span> <span class="st0">&quot;Email Address Obfuscator&quot;</span></div>
</li>
<li class="li1">
<div class="de1">description &nbsp;<span class="sy0">=</span> <span class="st0">&quot;Email Address Obfuscator&quot;</span></div>
</li>
<li class="li1">
<div class="de1">package &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="st0">&quot;karma-lab&quot;</span></div>
</li>
<li class="li1">
<div class="de1">version &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="st0">&quot;5.x-1.0&quot;</span></div>
</li>
<li class="li2">
<div class="de2">project &nbsp; &nbsp; &nbsp;<span class="sy0">=</span> <span class="st0">&quot;email_address_obfuscator&quot;</span></div>
</li>
</ol>
</div>
</div>
</div>
<p>   Et ensuite le corps du module lui-même :</p>
<div class='code-container-area'>
<div class='code-container-caption'>fichier email_address_obfuscator.module</div>
<div class='code-container'>
<div class="code">
<ol>
<li class="li1">
<div class="de1"><span class="kw2">&lt;?php</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp;</div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">/**</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp;* hook_help</span></div>
</li>
<li class="li2">
<div class="de2"><span class="coMULTI">&nbsp;*</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp;* @param string $section help section</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp;* @return i18n help</span></div>
</li>
<li class="li1">
<div class="de1"><span class="coMULTI">&nbsp;*/</span></div>
</li>
<li class="li1">
<div class="de1"><span class="kw2">function</span> email_address_obfuscator_help<span class="br0">&#40;</span><span class="re0">$section</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="kw1">switch</span> <span class="br0">&#40;</span><span class="re0">$section</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="st0">'admin/modules#description'</span> <span class="sy0">:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> t<span class="br0">&#40;</span><span class="st0">'Email Address Obfuscator'</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
</div>
</div>
<p>   Voilà, rien de plus n'est nécessaire pour déjà aller dans Administration du site/Constuction du site/modules et activer notre nouveau module.</p>
<p>Implémentation du filtre</p>
<p>   Un filtre s'est tout simplement un bout de code qui va prendre en entrée le texte du billet/commentaire, et fournir en sortie sa version, éventuellement modifiée. La première chose à faire est d'implémenter le hook_filter qui va effectuer ce travail. Comme le hook_block, cette fonction a plusieurs rôles : énumérer les filtres que le module expose, fournir leur description et modifier effectivement un texte. </p>
<p>Dans notre cas cela va donner le code suivant, à mettre à la suite dans email_address_obfuscator.module :</p>
<div class='code-container-area'>
<div class='code-container-caption'>à la suite dans email_address_obfuscato.moduler</div>
<div class='code-container'>
<div class="code">
<ol>
<li class="li1">
<div class="de1"><span class="kw2">function</span> email_address_obfuscator_filter<span class="br0">&#40;</span><span class="re0">$op</span><span class="sy0">,</span> <span class="re0">$delta</span><span class="sy0">=</span> <span class="nu0">0</span><span class="sy0">,</span> <span class="re0">$format</span><span class="sy0">=</span> <span class="nu0">-1</span><span class="sy0">,</span> <span class="re0">$text</span><span class="sy0">=</span> <span class="st0">''</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$op</span> <span class="sy0">==</span> <span class="st0">'list'</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <a target="blank" &nbsp;href="http://www.php.net/array"><span class="kw3">array</span></a> <span class="br0">&#40;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="nu0">0</span> <span class="sy0">=&gt;</span> t<span class="br0">&#40;</span><span class="st0">'eMail address obfuscator'</span><span class="br0">&#41;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; <span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">switch</span> <span class="br0">&#40;</span><span class="re0">$op</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="st0">'description'</span> <span class="sy0">:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> t<span class="br0">&#40;</span><span class="st0">'This is a simpe filter that every email address by something spammer prof.'</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="st0">'prepare'</span> <span class="sy0">:</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$text</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="st0">'process'</span> <span class="sy0">:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;<span class="kw1">return</span> <a target="blank" &nbsp;href="http://www.php.net/preg_replace"><span class="kw3">preg_replace</span></a><span class="br0">&#40;</span><span class="st0">&quot;@([A-Za-z0-9._-]+)<span class="es0">\@</span>([A-Za-z0-9._+-]+)<span class="es0">\.</span>([A-Za-z]{2,4})@se&quot;</span><span class="sy0">,</span> <span class="st0">&quot;_email_address_obfuscator_process('$1','$2','$3')&quot;</span><span class="sy0">,</span> <span class="re0">$text</span><span class="br0">&#41;</span><span class="sy0">;</span> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;</div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
</div>
</div>
<p>  La section process est assez simple. L'expression régulière passée à preg_replace va permettre de trouver toutes les adresses emails et les découper en trois éléments : non, domaine et tld. Pour chacune des occurrences, _email_address_obfuscator_process est appelé avec en paramètre les 3 éléments. </p>
<p>  Il nous faut donc ajouter maintenant cette fonction qui va créer la version modifiée de notre adresse email :</p>
<div class='code-container-area'>
<div class='code-container-caption'>à la suite dans email_address_obfuscator.module</div>
<div class='code-container'>
<div class="code">
<ol>
<li class="li1">
<div class="de1"><span class="kw2">function</span> _email_address_obfuscator_process<span class="br0">&#40;</span><span class="re0">$name</span><span class="sy0">,</span> <span class="re0">$domain</span><span class="sy0">,</span> <span class="re0">$tld</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">return</span> <span class="re0">$name</span><span class="sy0">.</span><span class="st0">&quot;_AT_&quot;</span><span class="sy0">.</span><span class="re0">$domain</span><span class="sy0">.</span><span class="st0">&quot;_DOT_&quot;</span><span class="sy0">.</span><span class="re0">$tld</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
</div>
</div>
<p>  Maintenant le plus gros est fait, il ne nous reste plus qu'à ajouter une petite fonction pour indiquer aux utilisateurs que toute adresse tapée sera protégée par le système. Pour cela, nous implémentons le hook__filter_tips :</p>
<div class='code-container-area'>
<div class='code-container-caption'>à la suite dans email_address_obfuscator.module</div>
<div class='code-container'>
<div class="code">
<ol>
<li class="li1">
<div class="de1"><span class="kw2">function</span> email_address_obfuscator_filter_tips<span class="br0">&#40;</span><span class="re0">$delta</span><span class="sy0">,</span> <span class="re0">$format</span><span class="sy0">,</span> <span class="re0">$long</span><span class="sy0">=</span> <span class="kw2">FALSE</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">switch</span> <span class="br0">&#40;</span><span class="re0">$delta</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">case</span> <span class="nu0">0</span> <span class="sy0">:</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">return</span> t<span class="br0">&#40;</span><span class="st0">'Toutes les adresses email saisies ici ne seront pas récupérables par les spammers.'</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; <span class="kw1">break</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
</div>
</div>
<p>Et voilà, c'est terminé. Il ne nous reste plus qu'à aller dans administration du site/configuration du site/formats d'entrée/configurer pour activer notre nouveau filtre par défaut pour tous les formats. Vous pouvez maintenant tester en créant un nouveau billet ou un nouveau commentaire et en mettant une adresse email dans le texte. Dans les aides, en dessous de la zone de saisie, vous devriez voir apparaître notre message d'aide précédent. Et lorsque vous validez, miracle, l'adresse est transformée. </p>
<p>   les filtres marchent pour les commentaires et les corps de node, pas pour leur titre !!!</p>
<p>  Pour des raisons de performances, les contenus filtrés sont pré-calculés et stockés en base. Si vous voulez protéger des textes existant, il faut exécuter le code SQL suivant :</p>
<div class='code-container-area'>
<div class='code-container'>
<div class="code"><span class="kw1">DELETE</span> <span class="kw1">FROM</span> cache_filter;</div>
</div>
</div>
<p>Pour les ultra-paranoïaques</p>
<p>  N'ayez crainte, j'en suis et je ne me contente pas vraiment de cette simple modification des emails qui peut être "cassée" de manière automatique. L'étape suivante est donc d'utiliser ce que nous venons de voir, mais avec un véritable encodage des adresses en PHP et leur décodage en javascript. </p>
<p>   Techniquement c'est un encodage simple (les ultra-ultra-paranoïaques peuvent ajouter un cryptage <img src="http://artisan.karma-lab.net/sites/all/modules/contrib/smileys/packs/crystal/wink2.gif" title="Wink" alt="Wink" class="smiley-content"/>, la chaîne de l'adresse va être transformée en une suite de code ASCII qui, côté client, va être décodée et insérer dans le page sous la forme d'un lien HTML. L'avantage par rapport à la méthode précédente, outre la sécurité supplémentaire, est que visuellement rien ne distinguera notre version encodée de la version non sécurisée. C'est par exemple le cas de l'adresse qui se trouve en introduction de ce billet.</p>
<p>   La premier chose que nous allons modifier est la ligne contenant l'expression régulière. Nous allons cette fois "matcher" toute l'adresse et appeler une autre méthode :</p>
<div class='code-container-area'>
<div class='code-container-caption'>en replacement de la ligne contenant preg_replace</div>
<div class='code-container'>
<div class="code"><span class="kw1">return</span> <a target="blank" &nbsp;href="http://www.php.net/preg_replace"><span class="kw3">preg_replace</span></a><span class="br0">&#40;</span><span class="st0">&quot;@([A-Za-z0-9._-]+<span class="es0">\@</span>[A-Za-z0-9._+-]+<span class="es0">\.</span>[A-Za-z]{2,4})@se&quot;</span><span class="sy0">,</span> <span class="st0">&quot;_email_address_obfuscator_encode('$1')&quot;</span><span class="sy0">,</span> <span class="re0">$text</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</div>
</div>
<p>  Maintenant passons à la procédure qui va transformer notre adresse en un lien en HTML contenant un mailto:, que nous allons encoder sous la forme d'un petit script JavaScript :</p>
<div class='code-container-area'>
<div class='code-container-caption'>à la suite dans email_address_obfuscator.module</div>
<div class='code-container'>
<div class="code">
<ol>
<li class="li1">
<div class="de1"><span class="kw2">function</span> _email_address_obfuscator_encode<span class="br0">&#40;</span><span class="re0">$address</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">$html</span><span class="sy0">=</span><span class="st0">&quot;&lt;a href='mailto:$address'&gt;$address&lt;/a&gt;&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">$result</span><span class="sy0">=</span><span class="st0">&quot;&lt;script&gt;document.write(String.fromCharCode(&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; <span class="kw1">for</span> <span class="br0">&#40;</span><span class="re0">$i</span><span class="sy0">=</span><span class="nu0">0</span><span class="sy0">;</span> <span class="re0">$i</span> <span class="sy0">&lt;</span> <a target="blank" &nbsp;href="http://www.php.net/strlen"><span class="kw3">strlen</span></a><span class="br0">&#40;</span><span class="re0">$html</span><span class="br0">&#41;</span><span class="sy0">;</span> <span class="re0">$i</span><span class="sy0">++</span><span class="br0">&#41;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="kw1">if</span> <span class="br0">&#40;</span><span class="re0">$i</span><span class="sy0">&gt;</span><span class="nu0">0</span><span class="br0">&#41;</span> <span class="br0">&#123;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; &nbsp; &nbsp; <span class="re0">$result</span><span class="sy0">.=</span><span class="st0">','</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; &nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li2">
<div class="de2">&nbsp; &nbsp; <span class="re0">$result</span><span class="sy0">.=</span><a target="blank" &nbsp;href="http://www.php.net/ord"><span class="kw3">ord</span></a><span class="br0">&#40;</span><span class="re0">$html</span><span class="br0">&#123;</span><span class="re0">$i</span><span class="br0">&#125;</span><span class="br0">&#41;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="br0">&#125;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="re0">$result</span> <span class="sy0">.=</span> <span class="st0">&quot;));&lt;/script&gt;&quot;</span><span class="sy0">;</span></div>
</li>
<li class="li1">
<div class="de1">&nbsp; <span class="kw1">return</span> <span class="re0">$result</span><span class="sy0">;</span> </div>
</li>
<li class="li1">
<div class="de1"><span class="br0">&#125;</span></div>
</li>
</ol>
</div>
</div>
</div>
<p>  A ce stade, il ne nous reste plus qu'une chose à faire, modifier la priorité des filtres dans administration du site/configuration du site/formats d'entrée/configurer/réordonner.  En effet chaque filtre associé à un format dispose d'une priorité qu'il convient de régler pour qu'ils ne rentrent pas en conflit les uns avec les autres. Dans notre cas, il est primordiale que le filtre HTML passe en premier (-10), puis notre filtre à adresses (-9), puis enfin le filtre URL (-8). Car si le filtre HTML passait après notre filtre, il désactiverais notre script, ce qui serait un peu dommage <img src="http://artisan.karma-lab.net/sites/all/modules/contrib/smileys/packs/crystal/wink2.gif" title="Wink" alt="Wink" class="smiley-content"/></p>
<p>  Une fois l'ordonnancement achevé, un petit coup de delete from cache_filter pour vider le cache et un F5 du côté navigateur. L'adresse de test que vous avez saisie au chapitre précédent devrait apparaître sous la forme d'un lien parfaitement visible. Pour terminer, faire un code de la page pour vérifier que l'adresse est bien illisible. On n'est jamais trop prudent <img src="http://artisan.karma-lab.net/sites/all/modules/contrib/smileys/packs/crystal/wink2.gif" title="Wink" alt="Wink" class="smiley-content"/></p>
<p>Conclusion</p>
<p>  Les filtres Drupal rendent d'énormes service et font gagner un temps fou. Et comme vous l'avez vu, on ne peut pas dire qu'ils soient d'une mise en oeuvre à se tordre le cerveau. C'est à la porté de n'importe qui sais coder trois lignes de PHP. De plus, vous pouvez ajouter autant de filtre que vous le désirez vu que tout est mis en cache et qu'au final cela n'a aucun impacte sur les performances. </p>
<p>  Les sources de ce module sont disponibles ici.</p>
    ]]></content>
  </entry>
</feed>
