Anti-Header-Injection

Sie sind überaus praktisch, nützlich, weit verbreitet und manchmal gefährlich: Kontaktformulare auf Webseiten.
Die ersten drei genannten Attribute sind sicher einleuchtend aber warum sind simple Kontaktformulare gefährlich, deren Aufgabe lediglich darin besteht, Nachrichten eines beliebigen Absenders an einen – meist fix eingestellten – Empfänger zu versenden?

Das Problem

Viele Kontaktformulare auf PHP-Basis prüfen die Eingaben der Nutzer nicht hinreichend und filtern gefährlichen Inhalt nicht aus, bevor er dem Mailserver zum Versand übergeben wird. Mir selber ist das vor einigen Jahren ebenfalls „passiert“, nachdem ich ein Versand-Skript aus einem PHP-Lehrbuch (!) übernommen hatte. Dort wurde an keiner Stelle auf mögliche Gefahren durch das unbedachte Verwenden der PHP mail()-Funktion hingewiesen. Werden die Eingaben nicht gefiltert, kann ein böswilliger Besucher zusätzliche Header-Informationen in die vermeintlich harmlose Mail einschleusen und so das Kontaktformular als Spam-Katapult verwenden. Damit Anderen nicht vielleicht dasselbe passiert, möchte ich hier das Verfahren der Header-Injection sowie einfache aber effektive Schutzmechanismen vorstellen.

Exkurs: Aufbau einer eMail

Auf wikipedia.de sind folgende Angaben zu eMail notiert:

Das Format einer E-Mail wird durch den RFC 2822 festgelegt. Danach bestehen E-Mails nur aus Textzeichen (7-Bit-ASCII-Zeichen). E-Mails sind intern in zwei Teile geteilt: Den Header mit Kopfzeilen und den Body mit dem eigentlichen Inhalt der Nachricht.

[…]

Die Header genannten Kopfzeilen einer E-Mail geben Auskunft über den Weg, den eine E-Mail genommen hat, und bieten Hinweise auf Absender, Empfänger, Datum der Erstellung und Stationen der Übermittlung.

Artikel E-Mail. In: Wikipedia, Die freie Enzyklopädie. Bearbeitungsstand: 3. März 2008, 12:43 UTC. URL: http://de.wikipedia.org/w/index.php?title=E-Mail&oldid=43251635 (Abgerufen: 3. März 2008, 20:04 UTC)

Der eigentliche Inhalt der eMail – der Body – ist hier eher unkritisch. Entscheiden für die Auslieferung der Mail und damit die Hebelstelle für potentielle Angreifer ist der Header der eMail, der in der Regel folgenden Angaben enthalten kann:

  • FROM – der Absender der eMail
  • REPLY TO – die Antwort-Adresse, kann vom FROM abweichen
  • TO – der Empfänger der eMail
  • CC – Kopieempfänger (engl. Carbon Copy)
  • BCC – Blinkopieempfänger (engl. Blind Carbon Copy)
  • SUBJECT – der Betreff
  • DATE – Versanddatum

Der Angriff – die Injection

Indem nun dem Nutzer gestattet wird, seine eMail-Adresse (FROM) einzutragen oder einen Betreff (SUBJECT) zu formulieren, gestattet man ihm gleichfalls die direkte Bearbeitung der eMail-Headers. Und damit gibt man das Ruder aus der Hand:

Der böswillie User könnte also in das Feld, welches eigentlich seine eMail-Adresse aufnehmen sollte (für Rückfragen z.B.) statt

meineadresse(at)irgendeinserver(dot)com

einfach folgendes eingeben:

meineadresse(at)irgendeinserver(dot)com\n
Cc: empfaenger2(at)irgendeinserver(dot)com, emfaenger2(at)andererserver(dot)com

Damit wird die eMail zusätzlich zu dem im Mail-Skript fest hinterlegten (beabsichtigten) Empfänger an zwei weitere Empfänger versandt. Zusammen mit der Option „Bcc:“ können auf diese Weise beliebig viele Empfänger adressiert werden – und das im Zweifelsfall ohne, dass der arglose Formular-Betreiber etwas davon mitbekommt.

Wenn es dann ganz dick kommt, bemerkt der Provider das Problem zuerst und nimmt kurzerhand den Webspace komplett oder in Teilen vom Netz (wie damals in meinem Fall). Zu dem nun bereits bestehenden Image-Schaden (als Absender der Spam-Mails taucht u.U. der Name des seriösen Website-Betreibers auf) steht man dann vor dem Problem: Was ist eigentlich passiert und was kann ich dagegen tun.

Nun die erste Frage sollte ja nun hinreichend beantwortet sein – machen wir uns also daran, das Skript abzusichern.

Kontaktformular absichern (Eingaben filtern)

Um den Missbrauch des Kontaktformulars verhindern und das Einschleusen fremden Codes in den Header zu unterbinden, gilt es alle Eingaben zu filtern – insbesondere die Angaben, die später im Header landen.

Hierfür stellt PHP bereits zwei sehr nützliche Funktionen zur Verfügung: preg_replace() und strip_tags(). preg_replace() ist mit der „Suchen und Ersetzen“-Funktion gängier Editoren vergleichbar, während strip_tags() zuverlässig PHP- und HTML-Code aus den Eingaben säubert.

Ein UNSICHERES Mail-Skript könnte z.B. so aussehen:

<?php
//Daten aus dem Kontaktformular uebernehmen
//Annahme: Daten werden mit method=POST versandt
$betreff=$_POST['betreff'];
$email=$_POST['email'];
$message=$_POST['message'];

//Mail erstellen und Absenden
mail("meinemail@irgendeinserver.de", "$betreff", $message, 
"From: $email\nReply-To: $email\nX-Mailer: PHP/" . phpversion(). "\nCC:");
?>

Hier wird – wie man leicht sieht – nichts gefiltert. Die Eingaben werden so wie sie aus dem Formular kommen direkt in die mail()-Funktion gepackt und dann an den Mailserver übergeben.

Und so könnte die GEFILTERTE Variante aussehen: 

<?php
  //Sonderzeichen aus Absender-eMailadresse filtern
  $email = preg_replace( "/[^a-z0-9 !?:;,.\/_\-=+@#$&\*\(\)]/im", "", 
  $_POST['email'] );

  //Spezielle-Mail-Befehle aus Absender-eMailadresse filtern
  $email = preg_replace( "/(content-type:|bcc:|cc:|to:|from:)/im", "", 
  $email );

  //Sonderzeichen aus Betreff filtern
  $betreff = preg_replace( "/[^a-z0-9 !?:;,.\/_\-=+@#$&\*\(\)]/im", "", 
  $_POST['betreff'] );

  //Spezielle-Mail-Befehle aus Betreff filtern
  $betreff = preg_replace( "/(content-type:|bcc:|cc:|to:|from:)/im", "", 
  $betreff );

  //Spezielle-Mail-Befehle aus dem Message-Text filtern
  //(landen zwar eh im BODY und nicht im HEADER - kann aber nicht schaden)
  $message = preg_replace( "/(content-type:|bcc:|cc:|to:|from:)/im", "", 
  $_POST['message'] );

  //...und letztendlich die gereinigte Mail erstellen
  mail("meinemail@irgendeinserver.de", "$betreff", $message, 
"From: $email\nReply-To: $email\nX-Mailer: PHP/" . phpversion(). "\nCC:");

?>

 

Ready to use – ich hab da mal was vorbereitet …

Da das separate Filtern jeder eingegeben Zeichenkette gerade bei umfangreichen Kontaktformularen etwas umständlich sein kann, habe ich ein kleines Skript vorbereitet, welches sich mittels include() einfach in das PHP-eMail-Skript integrieren lässt und anschliessend die Funktion „escapeStringForMail()“ bereitstellt.

Die Funktion erwartet den zu säubernden String und einen Indicator, der anzeigt ob alle unzulässigen Zeichen UND eMail-, PHP-, HTML-Befehle filtert oder NUR eMail-, PHP-, HTML-Befehle.

Ein Anwendungsbeispiel könnte so aussehen:

<?php
//Anti-Injection-Skript einbinden
include 'antiinjection.php';

//Daten aus dem Kontaktformular uebernehmen (besteht)
$betreff=$_POST['betreff'];
$email=$_POST['email'];
$message=$_POST['message'];

//Daten saubern (neu)
$betreff=EscapeStringForMail ($betreff, 1);
$email=EscapeStringForMail ($email, 1);
$message=EscapeStringForMail ($message, 0);

//Mail erstellen und Absenden
mail("meinemail@irgendeinserver.de", "$betreff", $message, 
"From: $email\nReply-To: $email\nX-Mailer: PHP/" . phpversion(). "\nCC:");
?>

Das Skript kann hier zur freien Verwendung heruntergeladen werden. Modifikation und Weitergabe sind ausdrücklich erlaubt und gewünscht. Ich bitte zu beachten, dass ich keine Gewähr für die einwandfreie Funktionalität des Skriptes übernehmen kann – der Einsatz erfolgt also auf eigene Gefahr!

antiinjection.php (zipped)

Wer Fehler findet und/oder Tipps zur Verbesserung hat, hinterlässt bitte einen Kommentar oder schreibt mir eine Mail.

Schreibe einen Kommentar

Personenbezogene Daten interessieren mich nicht – daher ist die Angabe von Name und E-Mail-Adresse freiwillig. Jedoch wird jeder Kommentar von mir geprüft, bevor er freigeschalten wird.