Barbacana

Ir a la zona de control

La lucha contra el spam en los comentarios de los blogs

Resumen del episodio anterior Desde finales del mes pasado, estamos recibiendo unos 500 mensajes diarios de propaganda no deseada. Por los motivos que ya se indicaron, esta práctica indeseable es perjudicial para la marcha de la web. Por tanto, hay que establecer medidas para intentar erradicarlas.

Cuando empecé a tomar consideración de la importancia del asunto, ya tenía 2000 mensajes en contra mia. Al igual que ocurre con el cirujano cuando corta, lo primero es apartar la sangre para poder ver la herida.

La versión de drupal que uso en la parte personal de la web (versión 4.6) no fue pensada para este tipo de ataques, así que el proceso que es necesario emplear para borrar un comentario (esté de forma pública o no) es el siguiente:

  • Ir a la lista de comentarios
  • Seleccionar el comentario para borrarlo
  • Visualizar que es el comentario que queremos borrar y confirmar el borrado

Hay que hacerlo individualmente, con lo que para más de 5 comentarios así es una auténtica tortura. En versiones más modernas (como la que está funcionando en este apartado técnico (drupal versión 4.7), se pueden seleccionar varios mensajes y borrarlos simultáneamente.

Así que el primer paso, es tener un sistema de borrado selectivo y automático de los mensajes de spam sin que me elimine, los comentarios válidos que puedan estar entre ellos. Para ello, ya que el gestor de contenidos no aporta las herramientas adecuadas, hay que meter las manos en el barro: tocar la base de datos directamente.

Me atrevería a decir que todo sistema de gestión de contenidos en la web (un programa que genera un portal web), necesita una base de datos para contener la información que va a mostrar. Sin entrar en marcas ni modelos, al final la base de datos ha de proporcionar servicios que simplificadamente se llaman «CRUD»: Creación, Recuperación, Actualización y Borrado (las siglas hacían referencias a las palabras en inglés Create,Retrieve,Update y Delete). Si el gestor de contenidos no permite borrado masivo, siempre se puede acceder directamente a la base de datos y generar una orden de borrado de los comentarios.

Borrado manual y selectivo
Observando el código fuente de drupal, cuando se produce el acto de borrar un comentario, se están borrando entradas de dos tablas: {comments} y {node_comment_statistics}. La primera tabla es la que almacena el comentario en sí y la segunda guarda información del tipo «el último comentario escrito fue el Martes día X».

Por simplificar el borrado, simplemente vamos a borrar la entrada correspondiente de la tabla {comments} e ignorar plácidamente las estadísticas, así que las estadísticas pueden hacer referencia a comentarios que han desaparecido y ya no existen (mal asunto, pero no lo peor que podría pasar).

Lo primero es entrar en la base de datos (mysql en la mayoría de instalaciones de drupal). En el fichero: sites/default/settings.php está el nombre de usuario, la contraseña y nombre de la base de datos a usar. Por supuesto, tocar incorrectamente en la base de datos significa una posible pérdida de todo el sitio web. Es obligación del lector contrastar la información que aquí se expone y en ningún caso asumo responsabilidad sobre los daños que el operador ocasione tras leer este texto:

$ mysql -u usuario -p basededatos

A continuación preguntará la contraseña. Se desaconseja emplear -pcontraseña porque queda archivado en el historial y cualquiera podria enterare de esa información.

Una vez dentro, la instrucción de borrado es:

delete from comments where homepage like '%pildorastontas.com%';

ojo, las primeras veces es conveniente comprobar que no vamos a borrar cosas que no queremos: así que lo primero sería ver que la cosa funciona:

select * from comments where homepage like '%pildorastontas.com%';

Y ahora explicación del comando sql. En ambos casos se borran/seleccionan entradas de la base de datos {comments} que cumplan la condición de tener el texto «pildorastontas» en el campo {homepage} (los signos de tanto porciento sirven para indicar que podría haber más texto antes y despues de nuestra palabra clave). Si queremos seleccionar por texto en el cuerpo del comentario, en vez de {homepage} se ha de usar {comment}. Tambien se podría seleccionar por el título del comentario, pero los spamers buscan títulos comunes para en primer instancia confundir a los administradores y podría ocurrir que se borrasen comentarios genuinos. Como lo que quieren es promocinarse, su página web ha de aparecer para que el spam sea efectivo y de ahí es donde agarrarse.

Aunque haya miles de comentarios, finalmente con tres o cuatro comandos como este, se puede uno eliminar todo el spam de comentarios de la web.

Para salir de mysql:

quit

Borrado automático, selectivo y más seguro
Cuando te das cuenta de que siguen llegando spam, no vas a estar borrando manual y diariamente lo que te llega. Tocar mucho en la base de datos directamente es peligroso y cuando se produzca el desliz puede tener resultados desastrosos. Así que desarrollé la idea expuesta por Nick Lewis (aunque al parecer la idea original era de Aldon Hynes, pero no la he encontrado en ningún lugar).

La este sistema presenta varias ideas interesantes:

  • No borra la base de datos directamente. En vez de ello, llama a la función de drupal que borra los comentarios. Por tanto el borrado es exáctamente idéntico a si se hace click en el botón de borrar.
  • Para no tocar el código de drupal, se crea una nueva página web llamada spam.php. Cuando se quiere borrar los mensajes, con que cualquier persona (sea el administrador o no) visite dicha página, automáticamente todo el blogspam desaparecera.
  • Ya que estamos, se puede programar un script que cada cierto tiempo, visite nuestra página spam.php y borre automáticamente los mensajes

El sistema está basado en una lista negra. Cuando entra comentarios de cierta dirección, lo único que hay que hacer es incluirlo en la lista y a continuación, siempre se borrará. (y todo usuario legítimo que tenga la misma web que una castigada tambien será eliminado).

Aquí está el código que actualmente empleo: (Nota: esta versión del script funciona para la versión 4.6 (y 4.7 creo). La versión del script para Drupal 5 está en un comentario más abajo).


<?php
include_once 'includes/bootstrap.inc';
include_once 'includes/common.inc';
/**/
$sql = "where ";
$sql.= "homepage like '%yourmed24.com%' or ";
$sql.= "homepage like '%pilloprice.com%' or ";
$sql.= "homepage like '%medshero.com%' or ";
$sql.= "homepage like '%diamond08.com%' ";
/**/
$ssql= "Select * from comments ".$sql;
$result= db_query($ssql);
while($comment = db_fetch_object($result)) {
print $comment->cid."<br>";
_comment_delete_thread($comment);
}
?>

Reflexiones

A partir de este momento, mi cola de moderación de comentarios está vacía. Aun así (y aunque esto pueda durar un tiempo), este sistema tiene el inconveniente de sobrecargar el servidor innecesariamente: primero dandole servicio a los spammers suministrandole mis páginas y almacenando sus anuncios y posteriormente borrándolos. Además, actualmente tengo poca variedad de «publicistas» con lo que es factible mantener una lista negra. Si estos crecieran mucho, volvería a ser un sistema inmanejable.

Lo ideal sería establecer filtros en la entrada que ni siquiera permitiese escribir el comentario en la base de datos si no cumple con ciertas garantías.

Los caminos que se plantean ahora son:

  • Instalar el módulo antispam de drupal. Este tiene el incoveniente (a priori, porque aun no lo he estudiado) que para enviar un comentario el servidor tenga tantas pruebas anti-spam que al final los recursos sean superiores al método actual.
  • Filtrar dentro de apache antes incluso de que llegue a drupal. Esto consumiría menos recursos pero posiblemente sea un sistema menos potente. Habría que ver si sería suficiente.

AntiSpam

Yo haria la validación en el modulo php que realiza el insert en la DB justo antes y sobre las variables de contenido que se cargan en la tabla en la sentencia INSERT. De esta forma puedes fltrar por contenido, hompage u otras palabras clave que desees eliminando la posibilidad de entrada y por tanto atacando el problema en el ultimo punto; La base de datos.

Yo lo llamaria filtrado de palabras clave o prohibidas

Espero haberme explcado y que te sea de ayuda.

Un cordial saludo.

No es bueno tocar un código que tu no mantienes

Efectivamente, el método ideal (y creo que lo menciono en algún lado) es no permitir que llegue el comentario a la base de datos. Ya que, en el método expuesto, aunque no se logra el objetivo del spam (publicidad gratuita para el anunciante), sigue perjudicando al servidor (cargando al mismo al usar la base de datos).

El por qué se ha implementado de una forma y no de la otra es por motivos de sencillez. El filtrado antes de la inclusión del spam en la base de datos requiere una de las siguientes opciones:

  • Añadir en el código de drupal (el gestor de contenidos que se usa aquí) los if correspondientes a palabras prohibidas
  • Usar un módulo anti spam de los que ya existen
  • Elaborar un módulo ligero y específico de filtrado

El primer caso es sencillo de implementar y una pesadilla de mantener. Ya he cometido antes el error de tocar código de drupal y cuando llega una actualización de seguridad hay que mirar línea a línea que se actualiza para ver si tu código tendrá interferencias de la actualización. El segundo caso funciona pero los módulos que he visto son muy buenos y hacen muchas cosas y un objetivo importante en el mantenimiento de este servidor es reducir la carga en lo máximo posible, por tanto esos módulos no se usarán en tanto soluciones ad hoc funcionen con menos requisitos. Y nos queda la tercera opción: para hacer un módulo (pasatiempo que también he practicado una vez), hay que mantener un interface determinado para comunicarlo con el core. Módulos de pequeñas líneas de código efectivo se convierten en cientos de líneas y ponerme a recordar los intringulis de la programación para drupal.

Por todas estas razones, se optó por una solución menos efectiva pero igualmente menos intrusiva con respecto a drupal y de fácil mantenimiento. En el caso de una carga masiva del sistema o de ataques de spam más inteligentes que no se detecten y eliminen adecuadamente se tendrá que reelaborar la estrategia a seguir, (que posiblemente sea buscar un módulo ya programado que haga la tarea).

Actualización a Drupal 5

Ha cambiado algo el sistema de acceso a las funciones internas en drupal. Así que es necesario tocar un poco el código:

<?php

include_once './includes/bootstrap.inc';
drupal_bootstrap(DRUPAL_BOOTSTRAP_FULL);

$sql = "where homepage = 'tonto.tu' ";
$sql.= " or homepage like '%besttechdirect.com%' ";
$sql.= " or homepage like '%diamond08.com%' ";
$sql.= " or homepage like '%diapers24.com%' ";
/* [...] */

$ssql= "Select * from comments ".$sql;
$result= db_query($ssql);
while($comment = db_fetch_object($result)) {
    print $comment->cid." ".$comment->nid."";
    _comment_delete_thread($comment);
    _comment_update_node_statistics($comment->nid);
}
?>

Los cambios son los siguientes:

  • Se incluye una función llamada drupal_bootstrap que permite determinar los recursos de drupal que queremos usar. Por ejemplo, si solo queremos acceso a la base de datos un DRUPAL_BOOTSTRAP_DATABASE nos hubiera bastado como argumento. Lamentablemente para no tener que tocar mucho la base de datos y usar funciones de más alto nivel hay que tener acceso al módulo "comment" y este no se carga hasta la última fase
  • Ya no es necesario cargar manualmente common.inc, ya que de eso se encarga drupal_bootstrap cuando es necesario.
  • hemos añadido una llamada a _comment_update_node_statistics que actualiza los datos mostrados de un nodo (el número de comentarios que tiene, por ejemplo)

Este sistema garantiza que el borrado del comentario es idéntico al que haga un administrador manualmente.