Vor einiger Zeit wurde ich Opfer von Spamer. Ich dachte, daß würde mir doch nicht passieren, da meine Scripte zum Mailversand nicht erlauben sollten, die Zieladresse anzugeben.
Mein Kontakt-Script wurde jedoch für die Spamer zur leichter Beute. Aber normalerweise ist bei dem Script nicht möglich, eine Zieladresse anzugeben. Das Formular nimmt nur die Empfänger-Adreese entgegen und schickt diese Mail dann weiter an mich. Wie konnte das denn passieren?
Zuerst ein kleiner Exkurs zur Mailversand mit sendmail.
Sendmail nimmt alle Angaben zur Mailadressen (Absender, Empfänger, CC, BCC)
und Inhalt als Textstream auf. Hier ein kleines und unvollständiges Beispiel:
sub sendmail {
my ($from, $to, $cc, $bcc, $subject, @body)=@_;
# Pfad zu sendmail
my $sendmail = "| /usr/sbin/sendmail -t -n -oi";
# jetzt die Adressen fuer sendmail vorbereiten
my $head = "";
$head .= "From: $from\n"; # Absender
$head .= "To: $to\n"; # Empfaenger
$head .= "Cc: $cc\n"; # CC
$head .= "Bcc: $bcc\n"; # Blindcopy
$head .= "Subject: $subject\n\n"; # Betreff
open(sm,$sendmail); # Sendmailqueue oeffnen
print sm $head; # Kopfdaten uebergeben
print sm @body; # Inhalt uebergeben
close sm; # Queue schliessen
}
Man könnte denken, alles sei in bester Ordnung. Man übergibt an die Funktion
die Parameter (z.B. die Mailadresse aus dem Kontaktformular als CC) und diese
erledigt die weitere Arbeit. Das tut sie auch, jedoch muß hier daran gedacht
werden, wie sendmail die Kopfdaten verarbeitet. Im Kopf stehen
dann z.B. (der Zeilenumbruch \n habe ich wegen der
Übersichtlichkeit entfernt):
From: admin@webbe.de To: you@domain.tld Cc: she@domain.tld
Bcc: he@domain.tld Subject: Testmail
Das Problem hier ist, wenn man die übergebenen Mail-Adressen nicht prüft, ob diese auch zwei
oder mehr Adressen in einem Parameterstring enthalten
# z.B. Aufruf der obengezeigte Funktion mit
sendmail('you\@domain.tld', 'admin\@webbe.de; copy\@webbe.de',
'', '', 'Testmail', 'Testmail von mir!');
oder die sendmail-Syntax enthalten
# z.B. Aufruf der obengezeigte Funktion mit
sendmail('From: bill\@gates.com
To: spam1@.domain.tld; spam2@.domain.tld; ... spam100@.domain.tld',
'admin@webbe.de', '', '', 'V I A G R A', 'Kaufen ...');
Eigentich ist der erste Parameter dafür vorgesehen den Absender z.B aus einem Kontaktformular festzulegen (siehe Funktionskopf)
und der zweite kommt direkte aus dem Script und ist im Kontaktscript festvorgegeben:
sendmail($mailFromContactFormular, 'admin@webbe.de' , '', '', $subjectFromContactFormular, $bodyFromContactFormular);Diese Mail wäre ganz normal verschickt worden und zwar nicht nur an den vorgesehenen Empfänger, sondern an die ganzen SPAM-Adressen.
sendmail-Syntax erlaubt. Also es emfiehlt sich alle Adressen, die man aus irgendwelchen
freien Benutzereingaben enthält zu untersuchen und bestimmte Syntax zu verbieten.
Zuerst gilt es eine korrekte Mailadresse zu erkennen. Dies könnte man mit regulären Ausdrücke erledigen (Quelle: http://aktuell.de.selfhtml.org/tippstricks/programmiertechnik/email/ von Christian Kruse):
#!/usr/bin/perl -w
use strict;
### RegEx begin
my $nonascii = "\x80-\xff"; # Non-ASCII-Chars are not allowed
my $nqtext = "[^\\\\$nonascii\015\012\"]";
my $qchar = "\\\\[^$nonascii]";
my $protocol = '(?:mailto:)';
my $normuser = '[a-zA-Z0-9][a-zA-Z0-9_.-]*';
my $quotedstring = "\"(?:$nqtext|$qchar)+\"";
my $user_part = "(?:$normuser|$quotedstring)";
my $dom_mainpart = '[a-zA-Z0-9][a-zA-Z0-9._-]*\\.';
my $dom_subpart = '(?:[a-zA-Z0-9][a-zA-Z0-9._-]*\\.)*';
my $dom_tldpart = '[a-zA-Z]{2,5}';
my $domain_part = "$dom_subpart$dom_mainpart$dom_tldpart";
# Der RegEx selbst
my $regex = "$protocol?$user_part\@$domain_part";
### RegEx end
# Kleiner Test-Schnipsel:
my @emails = (
'ckruse@wwwtech.de',
'"Christian Kruse"@wwwtech.de',
'"Christian \"CK\" Kruse"@wwwtech.de',
'"Christian"ckruse@wwwtech.de'
);
foreach my $email (@emails) {
if($email =~ /^$regex$/) {
print "it is a valid email\n";
}
else {
print "it isn't a valid email\n";
}
}
# eof
Danach sollte man bestimmte Syntax für Mailadressen verbieten. Dazu gehören z.B.
Zeilenumbruch oder die sendmail-Kommandos (To:, From:, Cc:, Bcc:):
Das alles kann man zusammenfassen:
sub checkMail {
my ($mail) = @_;
# Zeilenumbruch entfernen
chomp($mail);
# SPAM-Schutz: keine NEWLINE und keine Anweisungen
if (isEmail($mail) ne 1 || $mail =~ /\n/ || $mail =~ /(to|from|cc|bcc)\:\s+/i)
{
$mail = '';
}
return ($mail);
}
sub isEmail {
my ($mail) = @_;
### RegEx begin
my $nonascii = "\x80-\xff"; # Non-ASCII-Chars are not allowed
my $nqtext = "[^\\\\$nonascii\015\012\"]";
my $qchar = "\\\\[^$nonascii]";
my $protocol = '(?:mailto:)';
my $normuser = '[a-zA-Z0-9][a-zA-Z0-9_.-]*';
my $quotedstring = "\"(?:$nqtext|$qchar)+\"";
my $user_part = "(?:$normuser|$quotedstring)";
my $dom_mainpart = '[a-zA-Z0-9][a-zA-Z0-9._-]*\\.';
my $dom_subpart = '(?:[a-zA-Z0-9][a-zA-Z0-9._-]*\\.)*';
my $dom_tldpart = '[a-zA-Z]{2,5}';
my $domain_part = "$dom_subpart$dom_mainpart$dom_tldpart";
my $MailRegEx = "$user_part\@$domain_part";
### RegEx end
return $mail =~ /^$MailRegEx$/o;
}
Die Funktion checkMail bekommt die zu prüfenden
Mailadresse, entfernt, falls vorhanden, den Zeilenumbruch und prüft anschließend
ob in der Mailadresse Zeilenumbrüche oder sendmail-Anweisungen vorhanden sind.
Falls etwas zutrifft, wird die Mailadresse geleert.