Patch per fermare gli spambot in Drupal 7

Pubblicato da Nicola Rainiero il 21-03-2013 (aggiornato il 07-11-2013)

In questa ultima settimana sto testando una patch per il modulo captcha e un modulo personalizzato per bannare temporaneamente gli spambot che infestano il mio sito con Drupal 7. I risultati al momento sembrano essere promettenti, segue il codice usato e una breve descrizione del fenomeno spam che ho cercato di bloccare.

Nell'articolo precedente, Rallentare lo spam in D7 (I parte), ho descritto i moduli che uso per arginare lo spam. Sfortunatamente questi non sono sufficienti a risolvere completamente il problema. Infatti quando vado a visionare la pagina Top pages in the past 1 day (http://mio_sito/admin/reports/pages), trovo generalmente questo:

Top pages in the past 1 day (before the patch)
Top pages in the past 1 day (prima della patch)

Ma quanti commentatori! Così spesso devo risalire all'indirizzo IP responsabile e poi bannarlo manualmente. Perché ogni volta che il sito ricarica la pagina dopo un tentativo non riuscito, perdo una piccola quantità di banda mensile e questo mi scoccia molto! E poi non li digerisco proprio.

Patch per il modulo captcha

Ho trovato questa fantastica discussione in CAPTCHA » Issues e sebbene l'autore, Nightwalker3000, non la consideri una soluzione affidabile perché "It requires that the SPAMMER always using the same csid , but it seems like that there tools refresh the page after each try, so there get a new csid and then this patch doesn't work", (richiede che lo SPAMMER usi sempre lo stesso csid, ma sembra che esistano strumenti per ricaricare la pagina dopo ogni tentativo, così verrà assegnato di volta in volta un nuovo csid e la patch non riuscirà a funzionare a dovere), i miei test però rivelano che al momento lavora alla grande.

Ho inserito la patch (chiaramente l'ho ripulita dai caratteri e dal testo inutile) dentro il file captcha.module, a partire dalla linea 629, dopo questo:

          ),
WATCHDOG_NOTICE);
}

Ecco il codice (l'originale è qui):

//
// START new additions
//
// Retrieve the number of attempts and ip address for this session
$attempts_and_ip = db_query(
'SELECT attempts,ip_address FROM {captcha_sessions} WHERE csid = :csid',
array(':csid' => $csid)
)
->fetchAssoc();
// TODO - Make this configurable
$max_attempts=5;
// Ban IP if it has enter the Wrong Captcha for $max_attempts Times
if ($attempts_and_ip['attempts']>=$max_attempts) {
db_insert('blocked_ips')
->fields(array('ip' => $attempts_and_ip['ip_address'])) //188.165.240.76
->execute();
// log to watchdog
watchdog('CAPTCHA',
t('IP Adress %ip_address has been Blocked by CAPTCHA. Because of exceeding the Max Wrong Captcha Input of %maxattempts times'),
array('%ip_address'=>$attempts_and_ip['ip_address'],
'%maxattempts'=>$max_attempts));
form_set_error('captcha_response', t('Your IP has been Blocked. Please don\'t SPAM!'));
}
//
// END new additions
//

Come si può constatare dopo 5 tentativi perpetuati da questi IP bastardi, l'unico messaggio che potranno leggere sarà questo: "Sorry, XXX.XXX.XXX.XXX has been banned.".

L'esilio è purtroppo definitivo e non tiene conto magari di ripetuti ed involontari errori dell'utente (a proposito adesso ho semplificato l'immagine ed i caratteri del CAPTCHA, così questa scusa non può più reggere) oppure dell'assegnazione di quel IP ad un futuro ed ignaro navigatore. Per questi motivi ho pensato di aggiungere un modulo personalizzato che pulisce la tavola degli IP bannati dopo una esecuzione di cron.

Modulo clear iptable

Ho adattato questo esempio ed il mio floodclear.module è il seguente:

<?php
// $Id$

/**
* @file
* Experimenting with drupal's flooding mechanism.
*/
function floodclear_cron() {
// Default to an hourly interval. Of course, cron has to be running at least
// hourly for this to work.
$interval = variable_get('floodclear_interval', 60*60);
// We usually don't want to act every time cron runs (which could be every
// minute) so keep a time for the next run in a variable.

if (time() >= variable_get('floodclear_next_execution', 0)) {
// This is a silly example of a cron job.
// This clear your blocked ips
db_query("DELETE FROM `blocked_ips`");
//
watchdog('floodclear', 'floodclear ran');
if (!empty($GLOBALS['floodclear_show_status_message'])) {
drupal_set_message(t('floodclear executed at %time', array('%time' => date_iso8601(time(0)))));
}
variable_set('floodclear_next_execution', time() + $interval);
}
}

UPDATE (07/11/2013): Volendo mantenere alcuni indirizzi ip indesiderati dopo la cancellazione della tabella "blocked_ips" (per esempio "XXX.XXX.XXX.XXX" e "YYY.YYY.YYY.YYY") si può modificare il floodclear.module in questo modo:

<?php
// $Id$

/**
 * @file
 * Experimenting with drupal's flooding mechanism.
 */
function floodclear_cron() {
  // Default to an hourly interval. Of course, cron has to be running at least
  // hourly for this to work.
  $interval = variable_get('floodclear_interval', 60*60);
  // We usually don't want to act every time cron runs (which could be every
  // minute) so keep a time for the next run in a variable.

  if (time() >= variable_get('floodclear_next_execution', 0)) {
    // This is a silly example of a cron job.
    // This clear your blocked ips
    db_query("DELETE FROM `blocked_ips`");
    // Start add multiple disturbing ips
    $values = array(
      array(
        'ip' => 'XXX.XXX.XXX.XXX',
      ),
      array(
        'ip' => 'YYY.YYY.YYY.YYY',
      ),
    );
    $query = db_insert('blocked_ips')->fields(array('ip',));
       foreach ($values as $record) {
       $query->values($record);
    }
    $query->execute();
    // Finish add multiple disturbing ips
    watchdog('floodclear', 'floodclear ran');
    if (!empty($GLOBALS['floodclear_show_status_message'])) {
      drupal_set_message(t('floodclear executed at %time', array('%time' => date_iso8601(time(0)))));
    }
    variable_set('floodclear_next_execution', time() + $interval);
  }
}

Il modulo completo è in questo zip: floodclear.zip

Ho settato il tempo del cron nella pagina http://mio_sito/admin/config/system/cron e adesso viene eseguito ogni 12 ore. Non c'è dubbio che questa sia una soluzione provvisoria perché la guerra allo spam è una battaglia persa, tuttavia adesso le mie Top pages in the past 1 day e Top visitors in the past 1 day sono:

 Top pages in the past 1 day (after the patch) Top visitors in the past 1 day (after the patch)
Top pages in the past 1 day (dopo la patch)

Top visitors in the past 1 day (dopo la patch)

I primi due IP sono dei googlebot

Nel futuro se avrò di nuovo problemi con lo spam, tenterò un altro approccio più sofisticato, simile a quello usato dal modulo CAPTCHA After, ma con un funzionamento differente. In poche parole dopo X tentativi sbagliati dell'immagine captcha, i moduli per i commenti svaniscono e lo spambot si ritroverà con un pugno di mosche! Al momento ho solo il nome di questo fantomatico modulo: after_captcha (lo so ad immaginazione e fantasia non mi batte nessuno!).




Potrebbero interessarti anche:

Nicola Rainiero

Ingegnere civile specializzato in geotecnica con l'ambizione di facilitare la propria attività lavorativa usando e creando software libero per un sapere condiviso e collettivo. Mi occupo anche di energie rinnovabili ed in particolare di geotermia a bassa entalpia. Sono da sempre appassionato di web design e modellazione 3D.