Freitag, 23. Juni 2006
Sicheres Kontaktformular mit PHP - Spam verhindern
Viele Homepages besitzen ein Kontaktformular. Das man bei den meisten Formularen ohne Probleme Spam verschicken kann, wissen die wenigsten.
Das Problem ist, dass man unter Umständen den Mailheader verändern kann. Somit kann man dem Mailer ein wenig mehr unterschieben, als der Entwickler vorhatte und das Kontaktforumular wrid zum Spamformular...
Mailinjection Problembeschreibung und Lösungen
Auf vielen Homepages gibt es folgenden Programmcode, der eine Nachricht aus dem Kontaktformular versenden soll. Wir beschränken uns auf den Teil der die mail() Funktion von PHP aufruft.
Schlechter Code - so nicht!
Wir konzentrieren uns auf den letzten Parameter: 'FROM: '.$_POST['from'] - wo liegt genau das Problem? Ein Spammer oder eine andere böswillige Person kann jetzt Spam verschicken, indem er nicht nur seine Email einträgt, sondern auch noch etwas hinten dranhängt.
Spammer verwenden das Kontaktforumlar
Wenn jetzt folgendes in der $_POST['from'] steht: "spammer@spammingmakesmehappy.com \r\nBCC: spamopfer_1@kajh4ask.de; spamopfer_2@kajh4ask.de; spamopfer_1@kajh4ask.de; spamopfer_3@kajh4ask.de" werden Kopien der Nachricht an die Emailempfänger gesendet, welche nach dem BCC kommen. Somit kann man das Formular zum Spam versenden benutzen.
Lösungsansatz gegen Mailinjection
- function spamSafe($text) {
- }
- mail('webmaster@fakedomain42.com', $_POST['subject'], $_POST['message'], 'FROM: '.spamSafe($_POST['from']));
Somit werden die Zeichen, die eine Mailinjektion ermöglichen mit nichts ersetzt und das Kontakformular kann nicht mehr zum Spammen verwendet werden.
Erklärung des Regex gegen Mailinjection
Der Reguläre Ausdruck setzt sich folgedendermaßen zusammen:
- Die Slashs am Anfang und am Ende begrenzen den Regulären Ausdruck. Ist also eine Art Start, Stop Markierung.
- Das Pipezeichen | in der Klammer bedeutet eine logisches Oder, also OR
- \n ist ein Carriage Return
- \r ist ein Line Feed
- %0A ist die Hexadezimale Schreibweise für ein Linefeed LF
- %0D ist die Hexadezimale Schreibweise für ein Cariage Return CR
Die Hexadezimale Schreibweise wird angewant, falls der geschickte String Urlkodiert ist. Urlkodierte Strings werden vom Server wieder in normale Zeichen zurückinterpretiert.
Fertige Mailklasse
Ich habe jetzt euch eine kleine Mailklasse zur einfachen Verwendung geschrieben: PHP-Mail-Klasse mit Spamschutz
Kommentare (37) | Trackback (1)
Norbert - #1 - 29.06.2006 14:24 - (Antwort)
hallo - ich hätte gerne gewusst, wo ich die scripts für dieses mail-formular bekomme ?? - für eine schnelle antwort wäre ich dankbar. gruss norbert dollansky - info@it-netzwerk.de
Mr. Foo - #1.1 - 29.06.2006 15:40 - (Antwort)
Hallo Norbert,
was meinst du genau? Dieses Mailforumular gibt es nicht. Es ist fiktiver Beispielcode. Falls du aber ein komplettes Kontaktformularscript suchst kannst du mal unter hotscripts.com schauen. Da findest du sicher etwas.
Falls du noch Fragen hast, werde ich gerne Versuchen diese zu beantworten.
Mr. Foo - #2.1 - 01.02.2007 11:43 - (Antwort)
Hallo silvan,
freut mich, wenn der Quellcode dir geholfen hat. ![]()
Chris - #3 - 15.02.2007 14:43 - (Antwort)
Sehr cool! Werd mich gleich mal an mein Script ransetzen...
Ist es sehr kompliziert ein solches Verfahren mit dem grafischen Sicherheitscode einzubauen? Mfg Chris
Gabe - #4 - 24.05.2007 19:57 - (Antwort)
Bei deinem RegExp fehlt der erste Delimiter: --> / preg_replace("/(\n+|\r+|%0A|%0D)/i", '', $text);
Mr. Foo - #4.1 - 31.05.2007 22:34 - (Antwort)
Hi Gabe,
vielen Dank für den Hinweis. Irgendwie ist der Slash beim letzten Editieren verlohrengegangen ![]()
Frank - #5 - 17.06.2007 10:41 - (Antwort)
Hallo,
ich habe diese Funktion getestet, indem ich ein Formular mit dem oben genannten Absenderstring (spammer@spammingmakesmehappy.com \\r\\nBCC: spamopfer_1@kajh4ask.de; spamopfer_2@kajh4ask.de) gefüllt habe und diese Eingabe durch die spamSafe-Funktion schickte. In die habe ich noch Testausschriften eingebaut:
function spamSafe($text) {
echo $text . "";
$text = preg_replace("/(\n+|\r+|%0A|%0D)/i", '', $text);
echo $text . "";
return $text;
}
Dies sind die Ausschriften:
spammer@spammingmakesmehappy.com \\r\\nBCC: spamopfer_1@kajh4ask.de; spamopfer_2@kajh4ask.de
spammer@spammingmakesmehappy.com \\r\\nBCC: spamopfer_1@kajh4ask.de; spamopfer_2@kajh4ask.de
Ich sehe keinen Unterschied. Was mache ich falsch ? Was soll das preg_replace genau machen ?
Mr. Foo - #5.1 - 21.06.2007 22:07 - (Antwort)
Das liegt an der Option magic_quotes_gpc welche wahrscheinlich in der php.ini aktiviert ist. Die Funktion hat die Backslashes "escaped".
Das bedeutet, das beispielsweise Sonderzeichen, wie Zeilenumbrüche entwertet werden.
Du machst also nichts falsch in dem Sinne. Der reguläre Ausdruck in preg_replace sucht nach dem Zeilenumbruch, welcher aber nicht mehr gefunden wird, da der Backslash durch die oben genannte Funktionen entwertet wurde.
Webdesign - #6 - 05.11.2007 17:53 - (Antwort)
Die meisten Webdesigner machen die schönsten Kontaktformulare und vergessen leider immer wieder diese gegen Spam zu schützen. Wie oben beschrieben sind es gerade einmal zwei Zeilen PHP die, wenn sie implementiert würden tausende von Mailpostfächern vor SPAM verschonen würden.
Toller ZWEIZEILER !!
Holger
pr online - #7 - 04.01.2008 04:38 - (Antwort)
Meiner meinung nach ist die beste Methode um Spam zu verhindern immer noch Captcha ![]()
White - #7.1 - 07.01.2008 22:46 - (Antwort)
Ist aber weder Barrierefrei noch unlösbar für Bots mittlerweile. Und für viele einfach nur nervig.
Simple Fragen oder genauere Überprüfung der Eingaben sind da wesentlich besser.
Mr. Foo - #7.1.1 - 08.01.2008 00:17 - (Antwort)
Es geht eigentlich darum eine sogenannte Header-Injection zu verhindern...
... aber seis drum ![]()
White - #7.1.1.1 - 08.01.2008 17:41 - (Antwort)
Ist mir klar, meine Antwort war auf pr online bezogen ![]()
Robert - #8 - 02.02.2008 15:02 - (Antwort)
Hallo,
bin gerade dabei mich in die Materie einzuarbeiten ... Was spricht eigentlich dagegen, das Eingabefeld für die eMail Adresse mit z.B. maxlength="40" zu begrenzen?
MfG
Mr. Foo - #8.1 - 02.02.2008 15:11 - (Antwort)
Hallo Robert,
aus folgenden Gründen ist das nicht die beste Wahl:
1.) Emailadressen können länger sein.
2.) Diese Angabe ist nur eine Empfehlung für Browser
3.) Du versuchst wahrscheinlich einen Schutz Clienseitig zu implementieren. Das wäre der falsche Ansatz
Markus - #9 - 13.03.2008 16:27 - (Antwort)
Hey Sebastian,
sehr schöne sache. habe beim Einbinden allerdings noch ein Problem, vielleicht kannst du mir helfen, indem du mir kurz erläuterst, für was genau
"/(\n+|\r+|%0A|%0D)/i"
steht.
ganz speziell:
- warum beginnt der ausdruck mit einem /
- für was stehen die |
- für was steht %0A
?
gruß und danke,
markus
Mr. Foo - #9.1 - 13.03.2008 16:45 - (Antwort)
Hi,
ich habe oben den Artikel um die entsprechende Erklärung ergänzt.
Peter - #10 - 22.08.2008 10:06 - (Antwort)
Danke, genau nach sowas zum Einbauen hab ich gesucht.
Schöne Grüße.
Erich - #11 - 06.09.2008 04:31 - (Antwort)
Hallo Mr. Foo,
"spamSafe" muss auch für SUBJECT und MESSAGE verwendet werden, da sich darüber ebenfalls die Mail manipulieren lässt!
SUBJECT: "Hallo!%0ABcc:spammail@server.de"
Mr. Foo - #11.1 - 09.09.2008 11:44 - (Antwort)
Hi Erich,
vielen Dank.
Mir wäre es zwar neu, dass man auch über das Subject eine Headerinjection machen kann, aber ausschliessen möchte ich das nicht.
Ich werde aufjedenfall heute gleich mal überprüfen.
Erich - #11.1.1 - 11.09.2008 22:57 - (Antwort)
Eine Analyse der mir bekannten Injections habe ich online gestellt:
http://www.erich-kachel.de/?p=292
Du kannst übrigens die Injizierung eines "CC"-Parameters über den "Subject" mit diesem Code testen:
- $subject = "Test\r\n \nCc: spam_mich@anderer-server.de\n";
- $message = 'Hallo';
Mr. Foo - #11.1.1.1 - 24.01.2009 18:40 - (Antwort)
Du ich habe das übrigens in zich Variationen ausprobiert - leider konnte ich keine Headerinjection via Subject ausführen.
Danach habe ich mir den verantwortlichen PHP-Quellcode angeschaut:
- for(i = 0; subject_r[i]; i++) {
- if (iscntrl((unsigned char) subject_r[i])) {
- SKIP_LONG_HEADER_SEP(subject_r, i);
- subject_r[i] = ' ';
- }
- }
Wie man sehen kann durchläuft die For-Schleife das Subject. Wenn dann ein Steuerzeichen gefunden wird z.b CR, wird es durch ein Leerzeichen ersetzt.
Eine Mailinjection via Subject ist also unmöglich.
Mr. Foo - #12.1 - 17.11.2008 14:20 - (Antwort)
Das ist eine Variable, welche der Funktion übergeben wird.
Der Name ist ohne grössere Bedeutung gewählt worden.
Dennis - #13 - 23.01.2009 16:23 - (Antwort)
Ich habe versucht das in mein Formular einzubauen und bin als absoluter Laie gescheitert. Ich übergebe also diverse Variablen und speichere sie in $message. Jetzt will ich die kontakt-Variable prüfen, nach unerwünschten Zeichen, bcc und cc ausschließen sowie deine Funktion einbauen. Funktioniert aber nicht. Ich verstehe die preg_replace noch nicht so richtig und ob ich alles richtig übergebe und weiterverarbeite ist mir auch irgendwie unklar. Wäre nett, wenn einer helfen könnte. Solange probiere ich mal weiter.
Mr. Foo - #13.1 - 23.01.2009 16:57 - (Antwort)
Hi Dennis,
ohne deinen Code gesehen zu haben, wird es schwierig. ![]()
Dennis - #14 - 23.01.2009 17:05 - (Antwort)
- $message = $titel . "\n" . $autor . "\n" . $angaben . "\n" . $name . "\n" . $vorname . "\n" . $preis;
- $kontakt1 = preg_replace( "/[a-z0-9_-]+(\.[a-z0-9_-]+)*@([0-9a-z][0-9a-z-]*[0-9a-z]\.)+([a-z]{2,4}|museum)/i", "", $_POST['kontakt'] );
- /*
- $kontakt = preg_replace( "/(content-type:|bcc:|cc:|to:|from:)/im", "", $kontakt );
- $message = preg_replace( "/(content-type:|bcc:|cc:|to:|from:)/im", "", $message );
- function spamSafe($kontakt) {
- return preg_replace("/(\n+|\r+|%0A|%0D)/i", '', $_POST['kontakt'] );
- }*/
Hab diverses mal auskommentiert, da mir eine Fehlermeldung ausgegeben wird und hab jetzt vorne angefangen, aber bei der Überprüfung der e-Mail Adresse kann ich totalen Quatsch eingeben und die Mail kommt trotzdem an. Falls jetzt wieder kein Quellcode zu sehen ist, sagt mir bitte einer woran es liegen könnte.
Mr. Foo - #14.1 - 23.01.2009 18:53 - (Antwort)
Ich habe jetzt dir eine kleine Mailklasse geschrieben - siehe: http://mrfoo.de/archiv/741-Mailklasse-in-PHP-mit-Spamschutz.html
Versuch es am besten damit - wenn du damit Probleme hast - melde dich einfach wieder ![]()
Dennis - #15 - 23.01.2009 20:18 - (Antwort)
Beim allem Respekt und ich finds wirklich wahnsinnig nett, aber ich habe mir ne simple HTML Seite programmiert mit einem simplen Formular. Das ganze hat mich gut zwei Wochen Zeit gekostet. Meine Programmierkenntnisse sind also wirklich arg beschränkt. Vielmehr muss meine Webseite auch gar nicht können. Das Formular soll nur Spam sicher sein. Nach stundenlangem lesen und diversen Versuchen, wollte ich eben eine Badword-Überprüfung, die Verhinderung von bcc und cc, einen Captcha und wenns ganz luxuriös werden soll einen Zeitstempel. Ich scheitere nun schon an Punkt 1 quasi. Das mit der Mailklasse überfordert mich also so dermaßen, dass ich vermutlich mindestens weitere zwei Wochen brauche, um das ansatzweise zu verstehen. Mir wäre also einfach nur ein Hinweis recht, warum das nicht funktioniert was ich gemacht habe und wie es funktionieren könnte und zwar so erklärt, dass es einer wie ich im fortgeschrittenen Alter auch versteht.
Vielen Dank!
Mr. Foo - #15.1 - 24.01.2009 18:10 - (Antwort)
Du solltest preg_match anstatt preg_replace verwenden. In deinem Code ersetzt du eine richtige Emailadresse mit nichts.
Richtig wäre der Code ungefähr so:
- $message = $titel . "\n" . $autor . "\n" . $angaben . "\n" . $name . "\n" . $vorname . "\n" . $preis;
- //Wenn eine richtige Emailadresse übergeben wurde.
- if(preg_match("/[a-z0-9_-]+(\.[a-z0-9_-]+)*@([0-9a-z][0-9a-z-]*[0-9a-z]\.)+([a-z]{2,4}|museum)/i", $_POST['kontakt'] )) {
- mail("info@test.de","Nachricht von $_POST[vorname] $_POST[name]",$message . "\n".$_POST['kontakt'], 'From: '.$_POST['kontakt']);
- } else {
- // Falsche Emailadresse
- }
Dennis - #16 - 25.01.2009 10:37 - (Antwort)
Hallo,
ich habe jetzt die function SpamSafe einbauen können und Sie scheint zu funktionieren. Ich würde auch gerne die Prüfung der e-Mail Adresse einbauen, ob überhaupt eine eingegeben wurde. Aber das Bsp. mit preg_match wirft bei mir Fehler aus. Gibt es da Konflikte? Kann ich die if-else preg_match in die function spamSafe einfach mit reinbauen? Ich probiers einfach mal, mal sehen was passiert.
Grüße
Mr. Foo - #16.1 - 25.01.2009 18:15 - (Antwort)
Das liegt an der Blogsoftware. Diese ersetzt aus irgendeinem Grund die Anführungszeichen in das HTML-Sonderzeichen quot; - ich bin gerade bei der Problemlösung.
Zwischenzeitlich kannst du den relevanten Code einfach nehmen und das " gegen das " ersetzen - dann sollte es gehen.
Mario - #17 - 06.12.2009 10:57 - (Antwort)
Hallo.
Wie kann muss ich meinen Code ändern, um diese Schutzzeilen mit drinne zu haben:
// eigentlicher Mailversandt
mail($admin, $subject1, $message1, "From: $Email");
mail($admin1, $subject1, $message1, "From: $Email");
mail($Email, $subject2, $message2, "From: $admin");
Kriege irgendwie das nicht eingebaut, denn bei meinem Script sieht das leider so wie oben angegeben aus.
Gruß,
mario
Mr. Foo - #17.1 - 07.12.2009 12:14 - (Antwort)
- $Email = spamSafe($Email);
- // der Rest deines Codes
PS. Kann der User die Variablen $subject2 und $message2 beinflussen? Wenn ja, kann er trotzdem das Script verwenden um Spam zu versenden.

Aufgrund meines Artikels, sicheres Kontaktformular in PHP, habe ich eine kleine Mailklasse geschrieben, welche einfach zu benutzen ist und gegen Mailinjection-Attacken schützt.Download Code!class SimpleMailException extends Exception {} /**
Aufgenommen: Jan 23, 18:46