Lo hice y lo entendí

El blog de Vicente Navarro
24 feb

mailto: sin miedo a spammers

Todos sabemos que poner una dirección de e-mail en una página web es pasto seguro de spammers cuyos robots recorren incansablemente Internet en busca que cadenas que encajen con la siguiente expresión regular:

\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b

(Expresiones Regulares en la shell. Ejemplos de uso con grep, awk y sed.)

Para mostrarlo, he buscado alguna web que tuviera muchas direcciones de correo y la página de direcciones de contacto de la Universidad de Navarra resulta ser una excelente víctima para mi prueba. Con un comando como este:

$ wget -q -O - http://www.unav.es/informacion/direccionesdecontacto.html | \
egrep -oi '\b[A-Z0-9._%-]+@[A-Z0-9.-]+\.[A-Z]{2,4}\b' | sort -u | sed 's/unav.es/unav.is/'
abar@unav.is
adelgado@unav.is
adomeno@unav.is
agonzalez@unav.is
almcompr@unav.is
[...]

vemos lo fácil que resulta extraer todas las direcciones de correo de una página. Para el ejemplo, yo he tenido la precaución de cambiar el dominio final s/es/is/ para no dar aún más visibilidad a esas direcciones.

Por tanto, hemos de evitar poner nuestra dirección de correo electrónico visible a los robots de los spammers. Es muy común enmascarar la dirección de correo electrónico de forma que un humano la identifique fácilmente pero que a un robot le resulte difícil, como por ejemplo: “supercoco@exampleQUITAESTO.com“, “supercoco ARROBA example.com“, “supercoco EN example PUNTO com“, “supercoco (at) example (dot) com“, etc.

También es muy frecuente usar imágenes, ya que tampoco es común que los robots se paren a reconocer el texto que va en ellas. Yo, por ejemplo, en mi primera entrada de ¡Bienvenido a mi blog! , la puse así:

vinajo mail com

En E-Mail Icon Generator, te puedes generar imágenes como esta:

vinajo

para multitud de proveedores de correo que tienen predefinidos (e incluso si no lo tienen, pero en tal caso con menos florituras) que incluso te hospedan ellos para que la puedas usar en cualquier foro o página donde quieras dar tu dirección de e-mail sin miedo a robots.

Poco después de comprar el dominio, modifiqué el tema para que mostrara la nueva dirección de e-mail en el último de los recuadros que hay en la barra de la izquierda, el de debajo del calendario, con otra imagen:

Contacto

que creé con el comando convert de ImageMagick:

convert -pointsize 12 -size 140x40 -background white -transparent white -fill '#002F2F'\
-font /usr/share/fonts/truetype/ttf-bitstream-vera/Vera.ttf \
-gravity Center caption:'supercoco @example.com'

Dicha imagen nunca llevó enlace asociado de tipo mailto: en el tema anterior, pero para el nuevo tema decidí incluírselo y yo sabía que la única forma efectiva de hacerlo era con algo de código JavaScript, ya que lo más normal es que los robots de los spammers no se paren a interpretar el código JavaScript de la página. Pero hay que hacerlo sin cometer el fallo de meter la dirección en claro en el fichero .js de código JavaScript como he visto alguna vez hacer, porque los robots no interpretan el código pero sí que saben extraer las direcciones de los ficheros .js.

Mi forma de hacerlo es muy sencilla:

<ul class="sidebarul">
   <li class="buttons" id="contacto"><img alt="Contacto" title="Contacto" src="/misc/arroba08.png" /></li>
</ul>
<script type="text/javascript">
<!--
var ema = new Array("vic","&#64;","rro","master","ente",".com","nava","web","-");
var pos = new Array(7,3,1,0,4,8,6,2,5);
var dir = "";

for (i=0;i<ema.length;i++) dir = dir + ema[pos[i]];

contenido=document.getElementById("contacto");
contenido.innerHTML="<a href=\"mailto:"+dir+"\">"+contenido.innerHTML+"<\/a>";
// -->
</script>

Como veis, en el array ema pongo los diferentes trozos de la dirección de correo y en el array pos especifico en qué posición está el trozo adecuado para componer la dirección de correo. &#64; es el código HTML para @. En caso de que el código JavaScript esté deshabilitado, sólo se mostrará la imagen sin enlace mailto: asociado.

Es una técnica bastante sencilla, pero eficaz mientras no saquen un robot capaz de ejecutar el código JavaScript. Hay muchas, muchas, muchas, muchas otras formas de escribir código JavaScript que haga esto mismo, algunas muy sencillas, y otras muy sofisticadas.

Estaba yo documentándome un poco para esta entrada cuando de casualidad me ha llamado la atención este resultado de Google del Diario Época (de Argentina):

Diario Época - Contacto

Ese “Necesita Javascript para ver esta dirección de email” me ha hecho gracia y al ir a la página de contacto del Diario Época encuentro este código:

e-mail: Publicidad &lt;<SCRIPT LANGUAGE="javascript"><!--
ML="/h<pliufmc=d.t@ro:aeb\"> ";
MI="2BG1?C7:E8B54=@A36D4595;B;>;5B?5@C3@9B<9@8EF36D4595;B;>;5B?5@C3@9B<9@820BF";
OT="";
for(j=0;j<MI.length;j++){
OT+=ML.charAt(MI.charCodeAt(j)-48);
}document.write(OT);
// --></SCRIPT><NOSCRIPT>Necesita Javascript para ver esta dirección de email</NOSCRIPT>&gt;

Si nos fijamos, en la cadena ML tenemos todos los caracteres necesarios para confeccionar la cadena:

<a href="mailto:publicidad@diario...com">publicidad@diario...com</a>

Además, el método charAtCode nos da el código Unicode del carácter, así que al restarle 48 a cada carácter de la cadena MI obtenemos qué carácter de ML usar. Por ejemplo, 2 tiene el código Unicode 50, y al restarle 48 nos queda 2 que es el símbolo “<”; el B tiene el código 66 que menos 48 son 18, lo que nos lleva a la letra “a”, y así sucesivamente.

Como vemos, tiene bastante parecido al sistema que yo he usado. Para saber de dónde sale esta técnica sólo tenemos que buscar en Google ML.charAt y encontramos el Email Address Encoder de Rumkin.com, donde hay un formulario para generar las cadenas MI y ML para nuestra dirección de correo.

Pero el Diario Época comete un error, y es que es mala idea no mostrar la dirección de correo a los clientes sin JavaScript, sobre todo en una época como la actual en la que a menudo se aconseja el uso generalizado de extensiones como NoScript para evitar ciertas vulnerabilidades de seguridad de navegadores como Firefox (ejemplos: I, II) o en la que en ciertos entornos el Internet Explorer está configurado para no ejecutar por defecto código JavaScript. En caso de no haber JavaScript, podrían mostrar una dirección deformada como las que mostraba antes o una imagen, como hago yo.

En realidad, si a mí no me llega a escribir alguien sin JavaScript sobre algún tema del blog por pereza a escribir la dirección, tampoco es que vaya a perder dinero, pero no me parece muy buena idea poner ninguna dificultad al acceso de una dirección de correo que se usa para asuntos comerciales.

Graceful E-Mail Obfuscation

Y con esta reflexión aterrizamos en el quid de la entrada, una impresionante técnica que leí hace unos meses en A List Apart: Graceful E-Mail Obfuscation.

Consiste en convertir todas las direcciones e-mail que aparezcan en las páginas de nuestro sitio:

<a href="mailto:sales@yourcompany.com">
  E-mail our sales department
</a>

a algo como:

<a href="contact/sales+yourcompany+com" rel="nofollow">
  E-mail our sales department
</a>

Para hacer eso, se usa la siguiente función PHP en el servidor (la del artículo original no funciona directamente, así que ésta lleva unos pequeños retoques):

function encrypt_mailto($buffer) {
   return preg_replace("/[\"\']mailto:([A-Za-z0-9._%-]+)\@([A-Za-z0-9._%-]+)\.([A-Za-z.]{2,4})[\"\'\?]/","\"contact/\\1+\\2+\\3\" rel=\"nofollow\"",$buffer);
}

Para filtrar toda la página web con dicha función, podemos usar la función de PHP ob_start. He preparado la siguiente página de prueba para comprobar su efecto:

<?php
function encrypt_mailto($buffer) {
   return preg_replace("/[\"\']mailto:([A-Za-z0-9._%-]+)\@([A-Za-z0-9._%-]+)\.([A-Za-z.]{2,4})[\"\'\?]/","\"contact/\\1+\\2+\\3\" rel=\"nofollow\"",$buffer);
}
ob_start("encrypt_mailto");
?>
<html>
  <head>
  </head>
  <body>
    <a href="mailto:prueba@example.com">Correo de prueba</a>
  </body>
</html>
<?php
ob_end_flush();
?>

Tras cargarla, si visualizamos su código, veremos que la dirección de correo ha sido reemplazada por “contact/prueba+example+com”:

<html>
  <head>
  </head>
  <body>
    <a href="contact/prueba+example+com" rel="nofollow">Correo de prueba</a>
  </body>
</html>

El artículo nos explica que podemos configurar nuestro servidor para que todas las páginas, incluso las HTML (no sólo las PHP) pasen por este filtro sin tener que poner ob_start en cada página poniendo estas líneas en el fichero .htaccess y creando un fichero PHP que pase este filtro a cada página:

php_value auto_prepend_file /yourpath/prepend.inc.php
AddType application/x-httpd-php .php .htm .html

Sin embargo, según el hosting, es posible que dichas directivas no se puedan usar. Por ejemplo, yo en 1and1.es no puedo usar ni una ni otra y me encuentro con un error 500 si lo hago, mientras que en mis sistemas, no tengo problemas para usarlas.

Vale, ahora hemos cambiado el enlace con mailto: a un enlace con una URL rara, ¿para qué nos sirve eso?

Pues a los usuarios que tengan JavaScript habilitado les cambiaremos esa URL fea de vuelta a la dirección con mailto: poniendo el siguiente código al final de la página (esta versión es una adaptación mía del código del artículo):

<script type="text/javascript"><!--
var links = document.getElementsByTagName("a");
for (var l = 0 ; l < links.length ; l++) {
   var href = links[l].getAttribute("href");
   var address = href.replace(/.*contact\/([a-z0-9._%-]+)\+([a-z0-9._%-]+)\+([a-z.]+)/i , '$1' + '@' + '$2' + '.' + '$3');
   if (href != address) {
     links[l].setAttribute('href','mailto:' + address);
   }
}
--></script>

Por tanto, si a la página anterior le añadimos este código JavaScript, obtendremos la siguiente página perfecta para probar el camino de ida y el de vuelta (demo_mailto.php):

<?php
function encrypt_mailto($buffer) {
   return preg_replace("/[\"\']mailto:([A-Za-z0-9._%-]+)\@([A-Za-z0-9._%-]+)\.([A-Za-z.]{2,4})[\"\'\?]/","\"contact/\\1+\\2+\\3\" rel=\"nofollow\"",$buffer);
}
ob_start("encrypt_mailto");
?>
<html>
  <head>
  </head>
  <body>
    <a href="mailto:prueba1@example.com">Correo de prueba 1</a>
    <a href="mailto:prueba2@example.com">Correo de prueba 2</a>
    <script type="text/javascript"><!--
    var links = document.getElementsByTagName("a");
    for (var l = 0 ; l < links.length ; l++) {
       var href = links[l].getAttribute("href");
       var address = href.replace(/.*contact\/([a-z0-9._%-]+)\+([a-z0-9._%-]+)\+([a-z.]+)/i , '$1' + '@' + '$2' + '.' + '$3');
       if (href != address) {
          links[l].setAttribute('href','mailto:' + address);
       }
    }
    --></script>
  </body>
</html>
<?php
ob_end_flush();
?>

Y ahora viene la genialidad del asunto: si no se tiene JavaScript activado, como lo que tenemos es una URL como “contact/sales+yourcompany+com“, lo que hacemos es ¡atender realmente esa petición! Para ello, creamos una regla de reescritura de Apache (RewriteRule) que nos envíe estas peticiones a un script mail.php que puede atender la petición por ejemplo con un formulario para enviar un e-mail a esa dirección o con un CAPTCHA… cualquier cosa que identifique a un usuario real y descarte a los robots:

RewriteRule ^.*contact/([A-Za-z0-9._%-]*)\+([A-Za-z0-9._%-]*)\+([A-Za-z.]{2,4})$ mail.php?n=$1&d=$2&t=$3 [L]

Podemos ver cómo funciona todo junto en la página de demostración del artículo (tenemos que acordarnos de probarlo con JavaScript activado y con JavaScript desactivado), y también podemos descargar los ficheros necesarios para montar nosotros mismos esta demostración y aprender de los scripts ampliados (a diferencia del código que se muestra en el artículo que tiene pequeños fallos, éstos funcionan muy bien, porque son los mismos que los de su página de demo). De hecho, en la versión definitiva de la técnica que implementa el autor, además de todo lo anterior, usa una función ROT13 para ofuscar la URL y que tenga un aspecto como el siguiente:

<a href="contact/fnyrf+lbhepbzcnal+pbz" rel="nofollow">
  E-mail our sales department
</a>

Reflexión final

Hemos visto cómo proteger las direcciones de correo que tenemos publicadas en nuestras páginas de spammers con técnicas muy básicas (supercocoENexamplePUNTOcom, imágenes), con técnicas más sencillas y más complejas basadas exclusivamente en JavaScript y finalmente con una combinación de scripts en PHP y JavaScript y directivas de Apache que trabajando a la vez nos permitirán atender de forma efectiva a todos nuestros visitantes, tengan JavaScript o no.

Pero hemos de tener presente que aunque los robots no sean tan listos como para interpretar JavaScript, sus creadores, los spammers, saben al menos tanto como nosotros, por lo que estas técnicas no les son ajenas. Si una técnica es muy popular, es realmente trivial para ellos preparar a sus robots para que la identifiquen y extraigan la dirección e-mail.

Sabiendo el truco, es muy fácil extraer la dirección de e-mail de:

contact/fnyrf+lbhepbzcnal+pbz

o de:

ML="/h<pliufmc=d.t@ro:aeb\"> "

o de:

var ema = new Array("vic","&#64;","rro","master","ente",".com","nava","web","-");

incluso para un robot que no interpreta JavaScript.

Es por eso que mi consejo final es que no se use ninguna técnica que hayamos encontrado en Internet (como hacía el Diario Expansión) tal y como se nos presenta, sino que aprendamos de ella y la variemos aunque sea sólo ligeramente para que los robots no encuentren en nuestro código los patrones necesarios para identificar una técnica.

Actualización 28/12/09: anieto2k: 9 formas de ofuscar emails a prueba

:wq

Entradas relacionadas

19 Comentarios a “mailto: sin miedo a spammers”

  • Cleo dice:

    Buen artículo, Super Coco.

    Ahora que la has explicado, te tocará cambiar la técnica que usas en tu dirección de correo en “Lo hice y lo entendí” para evitar a los spammers que hayan podido leer tu artículo ;-) .

  • @Cleo ¡Gracias! Pues tienes mucha razón, pero ya lo había pensado, ya. De todas formas, si otros lectores siguen mis consejos, harán algunas variaciones sobre el código para no usar exactamente el mismo código ;-)

  • Bytecoders dice:

    Saludos SuperCoco.
    La cuestión entonces es quedarse con el concepto. Supongo que cambiando un poco la manera de “traducir” debería valer, no?
    El problema podría venir por un robot que pudiese actuar como un cliente con javascript (no sé si existe eso).

  • @Bytecoders Si un robot pudiera interpretar JavaScript, ninguna de estas técnicas se salvaría, porque si lo podemos ver en Firefox con JavaScript habilitado, también lo podría ver un supuesto robot con intérprete de JavaScript.

    Pero ahora pongámonos en la piel de un programador de un robot spammer. Si vemos que muchas páginas usan la técnica de A List Apart tal cual:

    contact/fnyrf+lbhepbzcnal+pbz

    nos es muy fácil intentar detectar URLs que empiecen por “contact” y tengan un par de signos de “+” y aplicarles un ROT13 para obtener la dirección.

    Si mucha gente usa la técnica de Rumkin.com, podemos hacer que el robot detecte la existencia de la línea:

    T+=ML.charAt(MI.charCodeAt(j)-48);

    y en tal caso, decodificar la dirección de e-mail a partir de MI y ML.

    Por tanto, el conocimiento de estas técnicas nos debe de valer para inspirarnos en ellas para implementarlas como mínimo con alguna variación menor: por ejemplo, cambiar el nombre de las variables, la forma de escribir las funciones, el tipo de cifrado, etc.

  • Raist dice:

    Un post interesante y necesario . Da asco la cantidad de basura que se puede llegar a recibir . Y lo que mas me extraña , es que en yahoo mismo , te creas una cuenta , no la usas para absolutamente nada , y en dos semanas tienes al menos 10 mensajes de spam , curioso si mas no .

  • Iván dice:

    Interesante el artículo. Yo opté por poner la dirección de contacto como una imagen y sin enlace. El que me quiera enviar un email tiene que escribir la dirección :-P

    Saludos, Iván.

  • @Raist, @Iván Gracias por vuestra visita y vuestros comentarios :P

  • Bytecoders dice:

    Bien, no creo que existan entonces robots tan avanzados como imagina mi retorcida mente :S
    Es por aquello de piensa lo peor, y acertarás. Siendo estas técnicas aún válidas no creo que sea algo común, supongo que todo se andará.
    Si al final me decido a poner la dirección de correo en la web ya miraré de hacer un apaño, tal y como tu dices con cambiar el orden ya debería bastar para que no le resultara familiar al robot.

  • @Bytecoders En realidad, fíjate en que llevamos todas estas precauciones para poner nuestro e-mail en una web y luego resulta que un conocido nuestro que te tiene en su libreta de direcciones pilla algún virus/gusano que recolecta direcciones de e-mail y adiós a tu cuenta sin spam.

    O, incluso peor… A alguien se le ocurre ponerte en un correo en cadena de esos con cientos de direcciones en correo sin usar la copia oculta y antes o después, tu dirección acaba en manos de un spammer…

    Pero al menos que no sea por nosotros ;-) .

  • Ringmaster dice:

    Gracias, SuperCoco! La verdad es que andaba tras del generador de imágenes de correo electrónico de Google y no lo encontraba!. Ahora ya puedo ponerla en el blog sin miedo.

  • Iñaki dice:

    Muy interesante, y como dicen en los comentarios: necesario.

    Un concepto en seguridad que está relacionado con el primer comentario, es la seguridad mediante oscuridad. Para juzgar la eficacia de un método, deberíamos pensar en si será válido incluso si los atacantes conocen su mecanismo. Esto es, si un método anti-spam funciona solo mientras a los spammers no se les ocurra que el JavaScript hay que interpretarlo (por decir algo), entonces es un método débil (se basa en seguridad mediante oscuridad).

    Si, por el contrario, un método se basa en interpretar un dibujo suficiente complejo (tal que no sea factible hacer un robot que lo interprete con rapidez y fiabilidad), el spammer puede saber perféctamente qué debería hacer para acceder al e-mail, pero no poder hacerlo. Por supuesto, un método que hoy no sea factible de romper, puede que mañana acabe siéndolo…

  • @Ringmaster Me alegro de que te haya servido.

    @Iñaki Estoy de acuerdo contigo pero también tenemos que pensar en la comodidad del usuario. Si nuestro objetivo es poner un enlace de tipo mailto: al que el usuario pueda hacer click con total facilidad, se abra su programa de correo electrónico favorito y te envíe el e-mail, no se me ocurre ningún método que no pase por usar JavaScript. Un CAPTCHA como el que sugieres tú es precisamente lo que usa la técnica del artículo de A List Apart en caso de un cliente sin JavaScript.

    Pero en realidad, a los spammers les resulta igual de difícil escribir código de robots capaces de hacer OCR a un CAPTCHA como capaces de interpretar JavaScript en general. Sin embargo, sí que es fácil para ellos hacer un compendio de técnicas populares y escribir justo el código necesario para reconocerlas. Es por eso que recomiendo escribir tu propio código basado en estas ideas. No es exactamente seguridad mediante oscuridad: Es seguridad basada en la incapacidad de los spammers para invertir importantes recursos técnicos en que sus robots descifren cualquier CAPTCHA o interpreten JS.

    ¡Muchas gracias por tu comentario!

  • bender dice:

    Otra técnica, también con JavaScript:

    http://www.spamspan.com/

    Saludos.

  • @bender ¡También muy interesante e instructiva! ¡Gracias por la aportación!

  • 9ºb dice:

    Muy buen articulo.

    Uno de los problemas de las direcciones embebidas en una imagen esque simplemente son inaccesibles y no pueden leerse por personas con discapacidad visual.

    Con estas tecnicas se mejora mucho.

    Gracias por el post y comentarte también que es admirable el blog

  • @9ºb ¡Gracias! Me alegro de que te haya gustado.

  • Mondo dice:

    Yo utilizo otra técnica que utilizaba cuando no tenía soporte para php y para realizar algo accesible desde el punto de vista de los que no utilizasen javascript. Consiste básicamente en poner la dirección al revés, es decir para mondo@pookmail.com
    en la página aparecería escrito moc.liamkoop@odnom y presentando de forma correcta con css y la indicaccion de dirección (direction:rtl;)

    más o menos quedaría así

    [CSS]
    .email {
    direction:rtl;
    unicode-bidi: bidi-override;
    }
    [HTML]
    <spam class=”email”>moc.liamkoop@odnom</spam>

    o tambien codificando la dirección (base64, por ejemplo) -incluso el mailto:// y el class-.

    P.D. Se podría haber evitado el nombrar a la clase email, que es uno de los parametros que utilizan los spambots para rastrear las posbles direcciones de correo, pero prefiero utilizarlo por la cuestión de los micrformatos (microformats.org)

  • @Mondo Pues es una alternativa realmente original. Jamás se me hubiera ocurrido hacer algo así. ¡Muy imaginativa!

    Muchas gracias por traerla aquí :-)

  • Bleyder dice:

    ¡muchas gracias por todo!

Tema LHYLE09, creado por Vicente Navarro