SPAM-Erkennung mit Bayes-Filter sicher optimieren

Abstract

Die Fähigkeit eines Spamfilters steigt bedeutend, wenn seine Bayes-Filter trainiert werden. Eine von vielen Möglichkeiten das Filter mit Spam- oder Ham-Mails zu füttern, ist diese per Mail (SMTP) zuzustellen. Der Vorteil dieser Lösung ist, sie ist schnell und einfach aufgesetzt.

Die Fähigkeit eines Spamfilters steigt bedeutend, wenn sein Bayes-Filter trainiert wird. Eine von vielen Möglichkeiten den Filter mit Spam- oder Ham-Mails zu füttern, ist diese per Mail (SMTP) zuzustellen. Der Vorteil dieser Lösung ist, daß sie schnell und einfach aufgesetzt ist.

Grundsätzlich funktioniert dieser Ansatz, indem SPAM-Nachrichten an ein lokales SPAM-Konto (hier: spam@spam.spam) und HAM-Nachrichten an ein HAM-Konto (hier: ham@ham.ham) gesendet werden. Hinter den Adressen verbirgt sich ein Skript, dass die Nachrichten entgegennimmt und diese an das Spamassassin-Kommando sa-learn übergibt. Dieses trainiert dann die Bayes-Datenbank (in der einen oder anderen Richtung) und erhöht damit die Erkennungsleisung des Bayes-Filter.

Als Erstes richte ich dazu passende Alias-Adressen in der /etc/aliases ein:

spam:          spam@spam.spam
ham:           ham@ham.ham

Zieladressen schützen

Damit nur bestimmte Absender an diese Ziel-Adressen senden können schütze ich diese Adressen mit einen Filter. Wer an diese Adressen senden möchte, der muss die - von mir gerade erfundene - Bedingung internal erfüllen. Eigenschaften dieser Bedingung sind: Der Sender ist entweder Teil der vertrauenswürdigen Netze oder SASL-Authentifiziert - alle anderen Sender werden abgelehnt. In main.cf schreibe ich dazu:

smtpd_restriction_classes =
    internal

internal =
    permit_mynetworks,
    permit_sasl_authenticated,
    reject

Als Nächstes sorge ich dafür, dass dieses Filter immer dann aufgerufen wird, wenn jemand an meine SPAM- und HAM-Adressen sendet. Ich lege dazu eine map /etc/postfix/recipient_access an, welche die Zieladressen mit meinem Filter verbindet:

spam@mail.servername.com   internal
ham@mail.servername.com    internal

Dann sorge ich dafür dass Postfix diese map auswertet, wenn ein Client einen Empfänger in der SMTP-Session übergeben hat. Ich rufe sie mit einem check_recipient_access-Test in main.cf auf:

smtpd_recipient_restrictions =
    ...
    check_recipient_access hash:/etc/postfix/recipient_access
    ...
    permit_mynetworks
    reject_unauth_destination
    ...

Bayesfilter trainieren

Jetzt sind die Bayes-Filter vor Mißbrauch geschützt und ich kann Nachrichten an diese Adressen zum Skript transportieren. Dazu lege ich mir einen transport in meiner /etc/postfix/transport an. Nachrichten an die Domains spam.spam und ham.ham werden an die Postfix-services sa-spam beziehungsweise sa-ham, die ich im nächsten Absatz erstellen werde, übergeben:

...
spam.spam       sa-spam:
ham.ham         sa-ham:
...

Die services definiere ich nun in der /etc/postfix/master.cf. Sie übergeben die Nachricht an mein Skript /usr/local/bin/sa-wrapper.pl:

#=========================================================================
# service type  private unpriv  chroot  wakeup  maxproc command + args
#               (yes)   (yes)   (yes)   (never) (100)
# ==========================================================================
...
#Spam & Ham
sa-spam unix    -       n       n       -       -       pipe
    user=vmail:vmail argv=/usr/local/bin/sa-wrapper.pl spam ${sender}
sa-ham  unix    -       n       n       -       -       pipe
    user=vmail:vmail argv=/usr/local/bin/sa-wrapper.pl ham  ${sender}

Der service sa-spam ruft das Skript mit dem Parameter spam auf und sa-ham tut dies unter Angabe von ham. So weiß sa-wrapper.pl was wie einzuordnen ist. Das Skript /usr/local/bin/sa-wrapper.pl sieht folgendermaßen aus:

#!/usr/bin/perl -w
# Time-stamp: <05 April 2004, 13:37 home>
#
# sa-wrapper.pl
#
# SpamAssassin sa-learn wrapper
# (c) Alexandre Jousset, 2004
# This script is GPL'd
#
# Thanks to: Chung-Kie Tung for the removal of the dir
#            Adam Gent for bug report
#
# v1.2
use strict;
use MIME::Tools;
use MIME::Parser;
my $DEBUG = 0;
my $UNPACK_DIR = '/tmp';
my $SA_LEARN = '/usr/bin/sa-learn';
#my @DOMAINS = qw/gtmp.org winnink.org/;
my ($spamham, $sender) = @ARGV;
sub recurs
{
   my $ent = shift;
   if ($ent->head->mime_type eq 'message/rfc822') {
       if ($DEBUG) {
       unlink "/tmp/spam.log.$$" if -e "/tmp/spam.log.$$";
       open(OUT, "|$SA_LEARN -D --$spamham --single >>/tmp/spam.log.$$ 2>&1") or \
           die "Cannot pipe $SA_LEARN: $!";
       } else {
       open(OUT, "|$SA_LEARN --$spamham --single") or die "Cannot pipe $SA_LEARN: $!";
       }
       $ent->bodyhandle->print(\*OUT);
       close(OUT);
       return;
   }
   my @parts = $ent->parts;
   if (@parts) {
       map { recurs($_) } @parts;
   }
}
#my ($domain) = $sender =~ /\@(.*)$/;
#unless (grep { $_ eq $domain } @DOMAINS) {
#    die "I don't recognize your domain !";
#}
if ($DEBUG) {
   MIME::Tools->debugging(1);
   open(STDERR, ">/tmp/spam_err.log");
}
my $parser = new MIME::Parser;
$parser->extract_nested_messages(0);
$parser->output_under($UNPACK_DIR);
my $entity;
eval {
   $entity = $parser->parse(\*STDIN);
};
if ($@) {
   die $@;
} else {
   recurs($entity);
}
$parser->filer->purge;
rmdir $parser->output_dir;

Note

Das Script habe ich für mein Setup etwas abgeändert, mein Dank geht an die Orginal Verfasser. Nun kann z.B. ein authentifizierter Mailuser eine falsch deklarierte SPAM Mail mittels seines Mailclients einfach an ham_at_mail.servername.com weiterleiten, oder umgekehrt eine nicht erkannte SPAM Mail an spam_at_mail.servername.com. diese Mails werden dann sozusagen pseudoautomatisch vom Spamassassin als entweder SPAM oder HAM erlernt.

Nachwort

Bitte vorliegendes Setup nicht einfach gedankenlos übertragen. Es muss grundsätzlich auf das eigene Setup angepasst werden, besonders der Schutz der eigenen lokalen User muss sichergestellt werden. Ich verwende z.B auf meinen Systemen praktisch nur virtuelle Mailadressen ( User ) und Domains mit Miltern. Ausserdem darf die Weiterleitung von SPAM und HAM an das Spamassassin Learn Script nur authentifizierten eigenen Mailusern erlaubt sein.

Des weiteren ist zu überlegen, wie die Mails dorthin weitergeleitet werden (sonst lernt das System, daß Mails vom internen Absender X immer Spam sind, nur weil dieser den Filter trainiert).

Robert Schetterer, 11. January 2014