<?
/*
* Sicherer Wrapper für
* bool mail ( string to, string subject, string message [, string additional_headers [, string additional_parameters]] )
*
* Unterschiede:
* 1) $to enthält kein @-Zeichen => Ungültige Mailadresse (Achtung, mehr wird hier nicht geprüft)
* 2) $to darf nur eine eMail-Adresse (@zeichen) enthalten
* 3) $to darf keinen Zeilenumbruch \n oder \r enthalten
* 4) $subject darf keinen Zeilenumbruch \n oder \r enthalten
* 5) $additional_headers darf kein @-Zeichen enthalten
* 6) $additional_headers darf kein \n\n, \r\r, \n\r\n\r oder \r\n\r\n enthalten
*
* Daraus ergibt sich z.B. die Einschränkung, das keine Bcc's oder Cc's mehr verschickt werden können.
* Wenn das nötig sein sollte, muß man im eigentlichen Mailaufruf $additional_headers durch
* "Bcc: mail@example.com\r\n".$additional_headers ersetzen werden. Die Variable $additional_headers
* darf auf keinen Fall mit ungeprüftem User-Input von Aussen gefüttert werden!
*
* Natürlich kann man auch einzelne Tests für sich deaktivieren, wenn man sich ganz sicher ist, das
* das User-Input gefiltert wurde. Den Test für $additional_headers sollte man aber auf keinen Fall
* entfernen.
*
* Return-Codes:
* <0 => Die Nummer vom Unterschied mit negativem Vorzeichen gibt an, welcher Test fehlgeschlagen ist
* 0: eMail konnte nicht verschickt werden, mail lieferte false zurück
* 1: eMail wurde verschickt, mail lieferte true zurück
*
*/
function mail_with_checks($to, $subject, $message, $additional_headers="", $additional_parameters="")
{
    
// CHECK: 1) $to enthält kein @-Zeichen => Ungültige Mailadresse (Achtung, mehr wird hier nicht geprüft)
    
$pos = strpos ($to, "@");
    if (
$pos === false) { return -1; }
    else
    {
        
// CHECK: 2) $to darf nur eine eMail-Adresse (@zeichen) enthalten
        
$pos = strpos ($to, "@", $pos+1);
        if (
$pos === false) {
            
// Scheint alles okay zu sein
        
}
        else
        {
            return -
2;
        }
    }
    
    
// CHECK: 3) $to darf keinen Zeilenumbruch \n oder \r enthalten
    
$pos = strpos ($to, "\n");
    if (
$pos === false)
    {
        
$pos = strpos ($to, "\r");
        if (
$pos === false) { // Scheint okay zu sein
        
}
        else
        {
            return -
3;
        }
    }
    else
    {
        return -
3;
    }
    
    
// CHECK: 4) $subject darf keinen Zeilenumbruch \n oder \r enthalten
    
$pos = strpos ($subject, "\n");
    if (
$pos === false)
    {
        
$pos = strpos ($subject, "\r");
        if (
$pos === false) { // Scheint okay zu sein
        
}
        else
        {
            return -
4;
        }
    }
    else
    {
        return -
4;
    }
    
    
// CHECK: 5) $additional_headers darf kein @-Zeichen enthalten
    
$pos = strpos ($additional_headers, "@");
    if (
$pos === false) { // Scheint okay zu sein
    
}
    else
    {
        return -
5;
    }
    
// CHECK: 6) $additional_headers darf kein \n\n, \r\r, \n\r\n\r oder \r\n\r\n enthalten
    
$pos = strpos ($additional_headers, "\n\n");
    if (
$pos === false)
    {
        
$pos = strpos ($additional_headers, "\r\r");
        if (
$pos === false)
        {
            
$pos = strpos ($additional_headers, "\n\r\n\r");
            if (
$pos === false)
            {
                
$pos = strpos ($additional_headers, "\r\n\r\n");
                if (
$pos === false)
                {
                    
// Scheint okay zu sein    
                
}
                else
                {
                    
// \r\n\r\n gefunden
                    
return -6;
                }
            }
            else
            {
                
// \n\r\n\r gefunden
                
return -6;
            }
        }
        else
        {
            
// \r\r gefunden
            
return -6;
        }
    }
    else
    {
        
// \n\n gefunden
        
return -6;
    }
    
    if (
mail($to, $subject, $message, $additional_headers, $additional_parameters)==true)
    {
        return
1;
    }
    else
    {
        return
0;
    }
}

echo
"<pre>";
// mail_with_checks($to, $subject, $message, $additional_headers="", $additional_parameters="")
// Tests für die Checks:
// 1) $to enthält kein @-Zeichen => Ungültige Mailadresse (Achtung, mehr wird hier nicht geprüft)
echo "Test 1a: ";
echo
mail_with_checks("Hallo Welt", "Hallo Welt", "Das ist eine eMail");
echo
"\nTest 1b: ";
echo
mail_with_checks("mail@example.tld", "Hallo Welt", "Das ist eine eMail");
// 2) $to darf nur eine eMail-Adresse (@zeichen) enthalten
echo "\nTest 2 : ";
echo
mail_with_checks("mail@example.tld,mail@example.tld", "Hallo Welt", "Das ist eine eMail");
echo
"\n";
// 3) $to darf keinen Zeilenumbruch \n oder \r enthalten
echo "Test 3 : ";
echo
mail_with_checks("mail@example.tld\r\nBcc: evil@example.tld", "Hallo Welt", "Das ist eine eMail");
echo
"\n";
// 4) $subject darf keinen Zeilenumbruch \n oder \r enthalten
echo "Test 4 : ";
echo
mail_with_checks("mail@example.tld", "Hallo Welt\r\nBcc: evil@example.tld", "Das ist eine eMail");
echo
"\n";
// 5) $additional_headers darf kein @-Zeichen enthalten
echo "Test 5 : ";
echo
mail_with_checks("mail@example.tld", "Hallo Welt", "Das ist eine eMail", "Bcc: evil@example.tld\r\n");
echo
"\n";
// 6) $additional_headers darf kein \n\n, \r\r, \n\r\n\r oder \r\n\r\n enthalten
/*
    Dieser Check ist der eigentliche Knackpunkt. Wenn man ungeprüft Formulareingaben vom Benutzer
    übernimmt, z.B. um ein schönes From zu basteln, kann genau dieses zur Gefahr werden.
    Nehmen wir an, das Formular bekommt $_POST["name"] und $_POST["email"] mit dem Namen und
    der eMail-Adresse des Absenders geposted. Wenn man jetzt ungeprüft daraus folgende
    $additional_headers Variable zusammenbaut, ist es für den Angreifer ein leichtes eigene
    SMTP-Anweisungen in den Header der eMail einzufügen (Achtung, so geht es NICHT!):
    $additional_headers="From: ".$_POST["name"]." <".$_POST["email"].">\r\n";
    Stellen Sie sich jetzt vor, das $_POST["name"] folgenden Inhalt hat (z.B. per Skript
    an die Seite geposted):
    $_POST["name"]="Evil User <evil@example.tld>\r\nBcc: spamopfer1@example.tld\r\nBcc: spamopfer1@example.tld\r\n\r\nDas hier ist der eingeschleuste eMail-Text, zum Beispiel eine Spam-Nachricht.";
    "\r\n\r\n" ist das Zeichen für den Ende des eMail-Headers. Der Text, der danach folgt,
    würde dann beim Empfänger der eMail tatsächlich angezeigt. Nungut, der Rest vom
    Formular kommt darunter auch noch. Das kann dem Spammer entweder egal sein, oder
    er führt die Injection mit einer MIME-codierten eMail durch, dann verschwindet der
    eigentliche Mailtext für den Benutzer von MIME-fähigen eMail-Programmen (was damit
    fast alle betrifft).
    Referer helfen hier als Schutz nicht weiter, die kann man mit einem eigenen Skript
    problemlos fälschen!
*/
echo "Test 6a: ";
echo
mail_with_checks("mail@example.org", "Hallo Welt", "Das ist eine eMail", "Evil User <evil@example.tld>\r\nBcc: spamopfer1@example.tld\r\nBcc: spamopfer1@example.tld\r\n\r\nDas hier ist der eingeschleuste eMail-Text, zum Beispiel eine Spam-Nachricht.");
echo
" (das Beispiel wird auch von Test 5 abgefangen)\n";
echo
"Test 6b: ";
echo
mail_with_checks("mail@example.org", "Hallo Welt", "Das ist eine eMail", "\r\n\r\n");
echo
"\n";
echo
"Test 6c: ";
echo
mail_with_checks("mail@example.org", "Hallo Welt", "Das ist eine eMail", "\n\r\n\r");
echo
"\n";
echo
"Test 6d: ";
echo
mail_with_checks("mail@example.org", "Hallo Welt", "Das ist eine eMail", "\r\r");
echo
"\n";
echo
"Test 6e: ";
echo
mail_with_checks("mail@example.org", "Hallo Welt", "Das ist eine eMail", "\n\n");
echo
"\n";
echo
"</pre>\n";
?>

Zurück zum Artikel - Security Corner - Kontakt / Impressum