Guerra allo Spam con i Plugin di SpamAssassin


Home Page | Commenti | Articoli | Faq | Documenti | Ricerca | Archivio | Storie dalla Sala Macchine | Contribuire | Imposta lingua:en it | Login/Register


Se, come me, anche voi avete come compito quello di gestire l'antispam per altra gente, saprete benissimo che la lotta allo spam e' un lavoro continuo. Questa settimana mi sono capitate una dozzina di mail segnalate come 'pulite' ma che ovviamente erano Spam.

Tutte le mail erano del tipo:


sequenza di parole apparentemente random

< blocco html di testo >
<a href="url.random">un click</a>

Una rapida analisi della cosa mi ha indicato che:

  1. La sequenza di parole iniziale e' troppo randomica e troppo corta perche' un sistema bayesiano possa riconoscerla
  2. Non ha senso usare un sistema di controllo del testo perche' e' random.
  3. Il dominio e' random, quindi come sopra

Dopo aver infilato la mia dozzina di spilli nella bambolina voodoo, ho guardato un po' meglio la cosa e mi sono accorto di un particolare significativo: la maggioranza di queste mail riportano si' un dominio apparentemente random, ma tutti questi domini fanno capo a 4-5 indirizzi IP.

Bene, cominciamo ad avere qualche cosa... ora, come faccio a controllare l'IP di un URL?

Dopo un po' di ricerche, mi sono reso conto che io posso si' controllare indirizzi IP usando una RBL, ma verificare un URL... no.

Ok, e' il momento di mettersi il cappellino da Baboon Coder e affrontare la cosa dal punto di vista programmativo. Ho finito pertanto con lo scrivere un Plugin per SpamAssassin.

SpamAssassin e' "estensibile" mediante l'uso di "plugin", che derivano da una struttura base e permettono di aggiungere controlli e funzionalita'.

Per creare un plugin per prima cosa si tratta di "importare" le funzionalita' base di ' Mail::SpamAssassin::Plugin'.

Questo ci da accesso a tutta una serie di funzioni ed informazioni riguardanti la mail che e' momentaneamente in scansione ed altre cose utili.

Un plugin "base" e' pertanto composto da:


#!/usr/bin/perl

package <nomedelpackage>

use Mail::SpamAssassin::Plugin;
use strict;
use bytes;

use vars qw(@ISA);
@ISA = qw(Mail::SpamAssassin::Plugin);

# constructor: register the eval rule
sub new {
	my $class = shift;
        my $mailsaobject = shift;

	# some boilerplate...
	$class = ref($class) || $class;
	my $self = $class->SUPER::new($mailsaobject);
	bless ($self, $class);

	$self->register_eval_rule("<regoladaregistrare>");

	return $self;
}

sub <regoladaregistrare> {
	...
}

Questo e' il minimo essenziale, ovviamente questo "plugin" non fa un tubo.

Dato che lo scopo della cosa e' interrogare un DNS ed avere l'IP partendo da un URI, quello che mi serve e' una funzione che interrogi un DNS.

Perl ha Net::DNS che fa' proprio al caso mio. Quello che ci serve quindi e' di aggiungere un bel use Net::DNS; all'inizio del nostro plugin e siamo quasi a cavallo.

Una semplice ricerca nel DNS viene effettuata con:


# now convert the URI into an IP
my $res   = Net::DNS::Resolver->new;

$res->udp_timeout(5);
$res->tcp_timeout(5);

my $query = $res->search($uri,'A');

if ($query) {
	foreach my $rr ($query->answer) {
		# this is not strictly necessary
		next unless $rr->type eq "A";
		return $rr->address;
	}
} else {
	return;
}

Dopo aver istanziato un 'resolver', imposto il timeout a 5 secondi, perche' o lo risolvo subito o non lo risolvero' mai. E non posso aspettare il timeout standard di 2 minuti mentre processo mail.

Dopo di che cerco tutti i record 'A' che corrispondono all'URI e ritorno il primo come indirizzo.

Notare che io ritorno subito il primo indirizzo che trovo, questo perche' se l'URI ha piu' di un indirizzo in genere e' un uri valido e non spam.

Adesso si tratta di aggiungere un po' di codice per rimuovere l'immondizia dai vari URI, tipo il protocollo, eventuali porte...

Quindi abbiamo un sistema per cercare l'IP corrispondente di un URI, adesso si tratta di confrontarlo con un elenco di IP forniti. Ma come?

Per questo ci viene in aiuto la funzione parse_config, che e' "ereditata" dal Plugin di base e viene chiamata automaticamente dalla registrazione del plugin.

La funzione legge il file di configurazione e carica una serie di 'regole' che vengono poi consultate.


sub parse_config {
	my ($self, $opts) = @_;
	my $key = $opts->{key};

	if ($key eq 'uriip') {
		if ($opts->{value} =~ /^(\S+)\s+(\S+)\s*$/) {
			my $rulename = $1;
			my $ip = $2;
			dbg("debug: URIIP: registering $rulename");
			$opts->{conf}->{uriip}->{$rulename} = $ip;
			$self->inhibit_further_callbacks();
			return 1;
		}
	}
	return 0;
}

Il codice sopra esposto cerca le regole chiamate 'uriip' e si aspetta di trovare il nome della regola come primo parametro e l'ip come terzo parametro.

Ora si tratta di eseguire il controllo. Per fare cio', usiamo la funzione parsed_metadata. Di nuovo, questa funzione e' ereditata da Plugin e viene chiamata automaticamente.


sub parsed_metadata {

	my ($self, $opts) = @_;
	my $scanner = $opts->{permsgstatus};

	# build a list of IPs converting the URIs
	my $reg;
	my %iplist = ();
	foreach my $uri ($scanner->get_uri_list()) {
		my $ip = my_uri_to_ip($uri);
		if ($ip) {
			dbg("debug: URIIP $ip for $uri");
			$iplist{$ip} = 1;
		}
	}
	# Now check if any match any defined rules.
	foreach my $rule (keys(%{$scanner->{conf}->{uriip}})) {
		my $ip = lc($scanner->{conf}->{uriip}->{$rule});
		if($iplist{$ip}) {
			dbg ("debug: URIIP hit rule: $ip");
			$scanner->got_hit($rule, "");
		}
	}

	return 1;
}

La funzione riceve un puntatore all'oggetto 'scanner', che contiene una serie di strutture che possiamo usare per scoprire cose relative alla mail in fase di processo. In particolare, la funzione get_uri_list ritorna un elenco degli URI trovati all'interno della mail. Si tratta quindi di passare ogni singolo URI alla nostra funzione di ricerca IP per sapere se abbiamo trovato una corrispondenza URI/IP oppure no, e se si, si abilita l'HIT usando la funzione got_hit.

Il file di configurazione per questo Plugin e' strutturato nel modo seguente:


uriip IP01      94.229.65.176
body  IP01      eval:check_uriip('IP01')
score IP01      8.0

uriip IP02      209.63.57.10
body  IP02      eval:check_uriip('IP02')
score IP02      8.0

Ovviamente 'IP01' ed 'IP02' sono nomi inventati.

Il codice sorgente contiene un file di configurazione di esempio. Per funzionare il plugin richiede Net::DNS. Se non lo avete dovrete installarlo.

Trovate il sorgente nell'archivio.

Per l'uso si tratta di copiare il plugin insieme agli altri Plugin di SpamAssassin, di solito ../lib/perl/<versione-perl>/Mail/SpamAssassin/Plugin/, quindi aggiungere nel vostro file di configurazione (di solito /etc/mail/spamassassin/init.pre)

loadplugin Mail::SpamAssassin::Plugin::URIIP

e quindi includere il file di configurazione in 'local.cf'.

Riavviare Spamd (se usate spamc/spamd) e provare.


I commenti sono aggiunti quando e soprattutto se ho il tempo di guardarli e dopo aver eliminato le cagate, spam, tentativi di phishing et similia. Quindi non trattenete il respiro.

Nessun messaggio this document does not accept new posts

Precedente Successivo

Davide Bianchi, lavora come Unix/Linux System Administrator presso una societa' di Hosting in Olanda.

Volete contribuire? Leggete come!.
 
 

Il presente sito e' frutto del sudore della mia fronte (e delle mie dita), se siete interessati a ripubblicare uno degli articoli, documenti o qualunque altra cosa presente in questo sito per cortesia datemene comunicazione (o all'autore dell'articolo se non sono io), cosi' il giorno che faccio delle aggiunte potro' avvisarvi e magari mandarvi il testo aggiornato.


Questo sito era composto con VIM, ora e' composto con VIM ed il famosissimo CMS FdT.

Questo sito non e' ottimizzato per la visione con nessun browser particolare, ne' richiede l'uso di font particolari o risoluzioni speciali. Siete liberi di vederlo come vi pare e piace, o come disse qualcuno: "Finalmente uno dei POCHI siti che ancora funzionano con IE5 dentro Windows 3.1".

Web Interoperability Pleadge Support This Project
Powered By Gojira