Kontaktformular absichern, damit man nicht zur SPAM-Schleuder wird – Mail-Header Injection

Schnell ist ein Kontaktformular, eine Weiterempfehlungsfunktion oder ähnliches programmiert. Allerdings hat man auch schnell versehentlich eine SPAM-Schleudern erstellt und der erste Spammer, der diese findet, freut sich.

Ungewollt SPAM versendet – was passiert und wie kann man es umgehen?

Ein Kontaktformular in PHP benötigt nur wenige Zeilen Code. Zur Veranschaulichung wird hier die einfachste (und unsicherste) Art erstellt.

Auf der HTML-Seite benötigen wir ein Formular

<form name="kontaktformular" action="absenden.php" method="get" enctype="text/html">
<p>Absender-E-Mail:<br>
<input type="text" name="absender" value="" size="20" maxlength="50">
</p>
<p>Betreff:<br>
<input type="text" name="betreff" value="" size="20" maxlength="50">
</p>
<p>
Ihre Nachricht:<br>
<textarea name="mailtext" rows="20" cols="20"></textarea>
</p>
<input type="Submit" name="" value="senden">
</form>

Und auf der PHP-Seite „absenden.php“ ist minimalistisch gesehen nur 1 Zeile notwendig:

mail ("empfaenger@spam-mich.tod", $_REQUEST['betreff'], 
      $_REQUEST['mailtext'], "From: ". $_REQUEST['absender'] );

Wenn jetzt das Argument kommt „das ist doch sicher, die Empfänger-Mail-Adresse empfaenger@spam-mich.tod ist fest kodiert. Wie soll diese geändert werden?“, dann ist es sehr kurz gedacht. Schauen wir uns erst einmal an, wie die nach dem entsprechenden Mail-Protokoll umgewandelte Mail aussieht. Nehmen wir dazu an, dass in den Formularfeldern eingetragen war:

/mail-zur-erklaerung.php?absender=ich@du.sie&
  betreff=Zwecks+XY &mailtext=hier+kommt+der+Text

Die wird nun in die PHP-Funktion, wie oben schon gezeigt, durch die Variablen übernommen

mail ("empfaenger@spam-mich.tod", $_REQUEST['betreff'], 
      $_REQUEST['mailtext'], "From: ". $_REQUEST['absender'] );

Aus diesen Informationen wird nun für das Mailprotokoll folgender Aufbau (was wichtig für das Verständnis der Verteidigungsart ist):

To: empfaenger@spam-mich.tod
Subject: Zwecks XY
From: ich@du.sie
hier kommt der Text

Wenn wir uns vorstellen, es würde ein weiterer Empfänger die E-Mail über BCC oder CC bekommen, würde es für das Mailprotokoll technisch wie folgt aussehen:

To: empfaenger@spam-mich.tod
Subject: Zwecks XY
From: ich@du.sie
Bcc: email@opfer.sie
hier kommt der Text

Technisch gesehen benötigen wir also nur einen Umbruch und den Text „Bcc: email@opfer.sie“

Und wenn wir uns obigen Aufbau mit Variablen anstatt dem Klartext ansehen, wird klar, wo die Schwachpunkte sitzen:

To: empfaenger@spam-mich.tod
Subject: $betreff
From: $absender
$mailtext

Beide Variablen sind die Schwachpunkte und können zur „Mail-Header Injection“ missbraucht werden. Dazu wird dann für den Umbruch (engl. line feed) das Sonderzeichen \n in Hexadezimalschreibweise %0A injiziert, gefolgt von Bcc:

/mail-zur-erklaerung.php?absender=ich@du.sie%0Abcc:email@
opfer.sie&betreff= Zwecks+XY &mailtext=hier+kommt+der+Text

Genauso könnte das auch über die Variable für den Betreff gefahren werden. Als Output gibt es nun 2 E-Mails: an den fest hinterlegten Empfänger empfaenger@spam-mich.tod und an email@opfer.sie

Dem Spammer ist herzhaft egal, wenn Sie viele E-Mail bekommen und diese u. U. unter Ihrem Namen und von Ihrem Server gesendet werden. Dass dieses Treiben illegal ist und einen größeren volkswirtschaftlichen Schaden hervorruft, ist unbestritten, allerdings ist der Weg zum Spammer teilweise weit, besonders, wenn dieser in irgendeinem Ausland sitzt.

Daher heißt die Device Spam-Schleudern vermeiden!

Wir haben also die üblichen Verdächtigen, auf die hin wir ALLE unsere Variablen untersuchen müssen! Dazu gehört dann %0A, bcc (Groß- und Kleinschreibung beachten!) , cc, to, content-type:, mime-version:, multipart/mixed, Content-Transfer-Encoding:, from:, Subject:, reply-to:

Es gibt mehrere Methoden herauszufiltern, ob einer der üblichen Verdächtigen eingeschleust werden soll. Dazu werden alle Übeltäter in ein Array gepackt und dann die Strings gegen das Array verglichen. Ist etwas faul – keine Mail senden!

function tuersteher_zum_verstehen( $zum_testen )
{
    $uebeltaeter[] = '%0A';
    $uebeltaeter[] = 'to:';
    $uebeltaeter[] = 'cc:';
    $uebeltaeter[] = 'bcc:';
    $uebeltaeter[] = 'from:';
    $uebeltaeter[] = 'subject:';
    $uebeltaeter[] = 'reply-to:';
    $uebeltaeter[] = 'content-type:';
    $uebeltaeter[] = 'mime-version:';
    $uebeltaeter[] = 'multipart/mixed';
    $uebeltaeter[] = 'content-transfer-encoding:';
    foreach ($uebeltaeter AS $einzeltaeter )
    {
        if (eregi ($einzeltaeter, $zum_testen))
        {
            echo "<p>Schauen Sie mal bei www.fang-den-hacker.de vorbei!

"
; // es wird also alles abgebrochen, wenn Gefahr in Verzug ist exit; } } } // und wer es kürzer mag, dasselbe kurz function tuersteher( $zum_testen ) { if ( preg_match("/(to:|cc:|bcc:|from:|subject:|reply-to:|content-type:|MIME-Version:|multipart/mixed|Content-Transfer-Encoding:)/ims", $zum_testen)) { echo "<p>Schauen Sie mal bei www.fang-den-hacker.de vorbei!</p>"; // es wird also alles abgebrochen, wenn Gefahr in Verzug ist exit; } if (preg_match("/%0A|\\r|%0D|\\n|%00|\\0|%09|\\t|%01|%02|%03|%04|%05|%06|%07|%08|%09|%0B|%0C|%0E|%0F|%10|%11|%12|%13/i", $zum_testen)) { echo "<p>Schauen Sie mal bei www.fang-den-hacker.de vorbei!</p>"; // es wird also alles abgebrochen, wenn Gefahr in Verzug ist exit; } }

Unsere PHP-Funktion mail werden also nur solchen Variablen übergeben, die unsere Funktion tuersteher() überlebt haben. Wobei sobald eine suspekter Eintrag auftaucht, nur noch der Text „Schauen Sie mal bei www.fang-den-hacker.de vorbei!“ auf dem Bildschirm angezeigt und das Programm abgebrochen wird.

Vor der Mail-Funktion also Variablen-Test:

$betreff =  tuersteher($_REQUEST['betreff']);
$absender = tuersteher  ($_REQUEST['absender']);
mail ("empfaenger@spam-mich.tod", $betreff, $_REQUEST['mailtext'], "From: ".$absender );

keine 100% Sicherheit

Und wie schon öfters im Kapitel erwähnt: Sollte es trotzdem Probleme geben, geben Sie nicht mir die Schuld. Es gibt keine 100% Sicherheit und es genügt nur eine einzige unsichere Programmstelle, um ein Loch in den Server zu reißen, und neue Möglichkeiten werden auch immer wieder gefunden. Unsere Vorgehensweise erhöht die Sicherheit – es kann Ihnen aber wahrscheinliche niemand 100% gewährleisten.

Und es gibt einiges, was man nicht glaubt, bevor man es gesehen hat. Es ist trotz hart codierter Empfänger-E-Mail möglich (mit den alten Löchern), diese unwirksam zu machen, und selbst wenn der Inhaltstext fix ist (wie z.B. bei einigen Weiterempfehlungsfunktionen), kann auch dieser ausgetauscht werden.


E-Mail und Sicherheit

Wenn von der PHP-Anwendung E-Mails versendet werden sollen, ist das technisch mit einem Befehl möglich. Allerdings sollte man da sehr sachte sein und nicht versehentlich eine Spam-Schleuder für Spammer erstellen. Oft ist nur notwendig, an eine bestimmte Adresse (bzw. definierte mehrere Adressen) eine E-Mail zu senden. Bei einer E-Mail-Adresse ist es einfach. Diese kann fest im PHP-Programm hinterlegt werden. Niemals (nie, nie, nie) sollte die Empfänger-Adresse im Formular in irgendeiner Form (meistens sieht man diese Sünden in Form eines Hidden-Fields) hinterlegt werden. Diese kann sehr einfach manipuliert werden und Schwupps ist man Spammer mit allen rechtlichen Folgen (allerdings ohne jegliche Einnahmen).

Soll das Script an mehrere Personen E-Mails senden, die Empfänger gegen eine fix hinterlegte Positivliste prüfen.

Im folgenden Beispiel erstellen wir für 3 Empfänger die Prüfung:

Ausgewählt wird im Formular der Empfänger anhand von einer Radio-Button-Auswahl (als Wert wird nur 1, 2 oder 3 übergeben).

Vor dem Absenden wird der Empfänger über die getroffene Auswahl zugeteilt:

switch( $empfaengerauswahl )
{
  case "1":
    $empfaenger = "elke@example.com";

    break;

  case "2":
    $empfaenger = "heike@example.com";

    break;

  ...

Spammer werden trotzdem versuchen, dieses Kontaktformular als Spam-Schleuder zu missbrauchen. Dies wird öfter vorkommen, als einem recht ist, aber man bekommt nur selber den Mist (und meistens auch nur 1-2 mal – der Spammer testet, ob das Formular missbrauchbar ist, und bei Misserfolg sucht er sich das nächste Opfer).

Weiterhin kann als weiteres Sicherheitsfeature die Anzahl der verschickten E-Mails begrenzt werden. Dazu kann man die Anzahl der Mails pro Stunde pro IP-Adresse festlegen und das PHP-Programm prüft, wie viele E-Mail in der letzten Stunde von dieser IP-Adresse gesendet worden ist.

Noch ein Schritt weiter ist, das Kontaktformular mit einem Captcha zu versehen und nur bei korrekt ausgefülltem Captcha zu versenden. Allerdings kann das potentielle Kunden schnell vergraulen und die Anzahl der Telefonate für Anfragen steigern, was i.d.R. angenehmer per E-Mail wäre.