Robert Bienert: /Email: Schutz vor Headerinjektion

Hallo Forum!

Ich bastele gerade an einer Mailfunktion, die z.B. auch in Kontaktformularen benutzt werden kann. Der Hintergrund, warum diese Abstraktion um mail() herumbaue liegt darin begründet, dass ich die Funktion nicht überall zur Verfügung habe und mit der Schnittstelle dann z.B. das Speichern in einer Datenbank implementieren kann, ohne den Code des Kontaktformulars großartig ändern zu müssen (nur einen Include austauschen).

Basierend auf meinen Erfahrungen und dieser Forumsdiskussion sollte doch folgender Ansatz einen ausreichenden Schutz bieten, oder:

  
function sendMail($fromName, $fromAddr, $toAddr, $subj, $text) {  
        /* Diese drei Werte koennen fuer Headerinjektionen benutzt  
         * und werden daher extra behandelt.  
         */  
        $subj   = str_replace(array("\n", "\r"), ' ', $subj);  
        $fromName = str_replace(array("\n", "\r"), ' ', $fromName);  
        $fromAddr = str_replace(array("\n", "\r"), '', $fromAddr);  
  
        return mail($toAddr, $subj, $text,  
                "From: \"$fromName\" <$fromAddr>");  
}  

Viele Grüße,
Robert

  1. Hi,

    /* Diese drei Werte koennen fuer Headerinjektionen benutzt
             * und werden daher extra behandelt.
             */
            $subj   = str_replace(array("\n", "\r"), ' ', $subj);
            $fromName = str_replace(array("\n", "\r"), ' ', $fromName);
            $fromAddr = str_replace(array("\n", "\r"), '', $fromAddr);

    Wenn diese drei Werte aus einzeiligen Formularfeldern stammen, deutet die Existenz von Zeilenumbruechen in ihren Inhalten doch mit an Sicherheit grenzender Wahrscheinlichkeit auf einen Manipulationsversuch hin.
    In dem Falle wuerde ich sie nicht "bereinigen" und anschliessend trotzdem noch die Mail versenden - sondern den Versand verweigern, und dem Nutzer (so er doch menschlicher Natur sein sollte, und kein Bot, was allerdings fuer verschwindend gering wahrscheinlich zu halten ist) einen entsprechende Fehlermeldung praesentieren.

    MfG ChrisB

    1. Moin!

      /* Diese drei Werte koennen fuer Headerinjektionen benutzt

      * und werden daher extra behandelt.
               */
              $subj   = str_replace(array("\n", "\r"), ' ', $subj);
              $fromName = str_replace(array("\n", "\r"), ' ', $fromName);
              $fromAddr = str_replace(array("\n", "\r"), '', $fromAddr);

      
      >   
      > Wenn diese drei Werte aus einzeiligen Formularfeldern stammen, deutet die Existenz von Zeilenumbruechen in ihren Inhalten doch mit an Sicherheit grenzender Wahrscheinlichkeit auf einen Manipulationsversuch hin.  
        
      Davon sollte man ausgehen. Außerdem lassen doch die Header für den Betreff und Absender auch nur eine Zeile als Wert zu, oder?  
        
      Aber was mich viel brennender interessiert: Gibt es darüber hinaus noch weitere gefährlich Zeichen, die man Herausfiltern oder (die Email) Verwerfen sollte?  
        
      Viele Grüße,  
      Robert
      
      1. Hi,

        Davon sollte man ausgehen. Außerdem lassen doch die Header für den Betreff und Absender auch nur eine Zeile als Wert zu, oder?

        Du denkst falsch - sprich in Zeilen. Headerdaten können Zeilenumbruch-Codes enthalten und dadurch weitere Headerzeilen einschmuggeln. Dies umgehst Du am einfachsten, indem Du Usereingaben ausschließlich in den mail-body schreibst.

        freundliche Grüße
        Ingo

        1. Hi,

          Du denkst falsch - sprich in Zeilen. Headerdaten können Zeilenumbruch-Codes enthalten und dadurch weitere Headerzeilen einschmuggeln.

          genau diese will er doch eleminieren.

          Dies umgehst Du am einfachsten, indem Du Usereingaben ausschließlich in den mail-body schreibst.

          Du kennst dich mit der Materie wesentlich besser aus als ich, aber wie soll das denn bei der Absenderangabe funktionieren?

          BTW: Die potenziell "gefährliche" Benutzerangabe von Absendername und -adresse ist doch im Normalfall ein Name und eine Emailadresse. Beides enthält (meines Wissens nach) nie einen Doppelpunkt. Dieser wiederum ist aber in jeder Header-Angabe erforderlich. Wäre es demnach nicht eine Möglichkeit, neben den Zeilenumbrüchen, auch nach dem Vorhandensein eines solchen zu suchen, und dann die Eingabe nicht zu akzeptieren/verarbeiten?

          Gruß Gunther

          1. Hi,

            BTW: Die potenziell "gefährliche" Benutzerangabe von Absendername und -adresse

            Subject ist genauso "gefaehrdet".

            ist doch im Normalfall ein Name und eine Emailadresse. Beides enthält (meines Wissens nach) nie einen Doppelpunkt. Dieser wiederum ist aber in jeder Header-Angabe erforderlich. Wäre es demnach nicht eine Möglichkeit, neben den Zeilenumbrüchen, auch nach dem Vorhandensein eines solchen zu suchen, und dann die Eingabe nicht zu akzeptieren/verarbeiten?

            Im Subject dann konsequenterweise auch?
            Dann koennte/duerfte ich dir also keine Mails mehr schreiben, in denen "Unser Preisangebot: 345,- EUR" o.ae. steht ...?

            MfG ChrisB

            1. Hi,

              BTW: Die potenziell "gefährliche" Benutzerangabe von Absendername und -adresse

              Subject ist genauso "gefaehrdet".

              ja, aber von dem sprach ich nicht. Dafür muss man sich etwas anderes überlegen, da dort benutzerseitig ja durchaus ein Doppelpunkt vorkommen kann (siehe dein Beispiel).

              ist doch im Normalfall ein Name und eine Emailadresse. Beides enthält (meines Wissens nach) nie einen Doppelpunkt. Dieser wiederum ist aber in jeder Header-Angabe erforderlich. Wäre es demnach nicht eine Möglichkeit, neben den Zeilenumbrüchen, auch nach dem Vorhandensein eines solchen zu suchen, und dann die Eingabe nicht zu akzeptieren/verarbeiten?

              Im Subject dann konsequenterweise auch?
              Dann koennte/duerfte ich dir also keine Mails mehr schreiben, in denen "Unser Preisangebot: 345,- EUR" o.ae. steht ...?

              Für die Subject-Eingabe könnte man jenachdem auch eine Auswahlliste verwenden (und die Auswahl natürlich trotzem noch überprüfen/ abgleichen vor der Weiterverarbeitung).

              Gruß Gunther

          2. Hallo

            BTW: Die potenziell "gefährliche" Benutzerangabe von Absendername und -adresse ist doch im Normalfall ein Name und eine Emailadresse. Beides enthält (meines Wissens nach) nie einen Doppelpunkt.

            Du irrst. Siehe RFC2822, ich zitiere:

            <zitat>
               2.2. Header Fields

            Header fields are lines composed of a field name, followed by a colon
               (":"), followed by a field body, and terminated by CRLF.  A field
               name MUST be composed of printable US-ASCII characters (i.e.,
               characters that have values between 33 and 126, inclusive), except
               colon.  A field body may be composed of any US-ASCII characters,
               except for CR and LF.  However, a field body may contain CRLF when
               used in header "folding" and  "unfolding" as described in section
               2.2.3.  All field bodies MUST conform to the syntax described in
               sections 3 and 4 of this standard.
            </zitat>

            Während der Doppelpunkt im Headerfeldnamen nicht vorkommen darf, ist der Doppelpunkt im Body erlaubt.

            Bitte beachte, dass der local-part einer E-Mail-Adresse ein quoted-string sein kann und daher auch den Doppelpunkt enthalten kann, siehe diesen Archivbeitrag von Cheatah und Validierung von E-Mail-Adressen im Selfforumssieb.

            Dieser wiederum ist aber in jeder Header-Angabe erforderlich. Wäre es demnach nicht eine Möglichkeit, neben den Zeilenumbrüchen, auch nach dem Vorhandensein eines solchen zu suchen, und dann die Eingabe nicht zu akzeptieren/verarbeiten?

            Nein. Du willst also viele zulässige E-Mail-Adressen und Headerinhalte ausschließen. Das ist keine gute Idee.

            Freundliche Grüße

            Vinzenz

            1. Hallo!

              BTW: Die potenziell "gefährliche" Benutzerangabe von Absendername und -adresse ist doch im Normalfall ein Name und eine Emailadresse. Beides enthält (meines Wissens nach) nie einen Doppelpunkt.

              Du irrst. Siehe RFC2822, ich zitiere:

              <zitat>
                 2.2. Header Fields

              Header fields are lines composed of a field name, followed by a colon
                 (":"), followed by a field body, and terminated by CRLF.  A field
                 name MUST be composed of printable US-ASCII characters (i.e.,
                 characters that have values between 33 and 126, inclusive), except
                 colon.  A field body may be composed of any US-ASCII characters,
                 except for CR and LF.  However, a field body may contain CRLF when
                 used in header "folding" and  "unfolding" as described in section
                 2.2.3.  All field bodies MUST conform to the syntax described in
                 sections 3 and 4 of this standard.
              </zitat>

              Während der Doppelpunkt im Headerfeldnamen nicht vorkommen darf, ist der Doppelpunkt im Body erlaubt.

              Der Punkt ist doch, dass zwischen dem field name und dem field body _immer_ ein Doppelpunkt stehen _muss_. Daher wäre für mich unter dem Sicherheitsaspekt die Eingabe eines Doppelpunkts seitens des Users im Formular ein Ausschlusskriterium.

              Bitte beachte, dass der local-part einer E-Mail-Adresse ein quoted-string sein kann und daher auch den Doppelpunkt enthalten kann, siehe diesen Archivbeitrag von Cheatah und Validierung von E-Mail-Adressen im Selfforumssieb.

              Dieser wiederum ist aber in jeder Header-Angabe erforderlich. Wäre es demnach nicht eine Möglichkeit, neben den Zeilenumbrüchen, auch nach dem Vorhandensein eines solchen zu suchen, und dann die Eingabe nicht zu akzeptieren/verarbeiten?

              Nein. Du willst also viele zulässige E-Mail-Adressen und Headerinhalte ausschließen. Das ist keine gute Idee.

              1. Wieso Headerinhalte? Die Benutzer _sollen_ ja wohl _keine_ Headerangaben selbst in das Formular eintragen.
              2. Und ja, im local part einer Emailadresse wäre auch der Doppelpunkt erlaubt (allerdings müsste der local-part dann entweder in doppelte Anführungsstriche eingeschlossen werden oder der Doppelpunkt durch einen vorangestellten Backslash escaped werden).

              Und genau hier sehe ich einen gravierenden Unterschied zwischen Theorie und Praxis, bzw. den Sicherheitsanforderungen eines Webmasters.

              Und ja, ich sehe es durchaus als vertretbar an, wenn man Emailadressen, die einen Doppelpunkt enthalten, zurückweist (zumindest bei einem Kontaktformular)! Denn wie oft wird daran das Zutsandekommen einer Kommunikation in der Praxis wirklich scheitern? Kann man nicht eher davon ausgehen, dass User mit einer solchen Emailadresse (mit Doppelpunkt) auch noch mind. eine Alternative ohne haben? Ich glaube schon, denn ansonsten dürften diese Leute eh des öfteren erhebliche Probleme haben, da es seit Jahren durchaus gängige Praxis im Web ist, Adressen in dieser Form zu überprüfen. Und da spielen die RFCs eben keine Rolle.

              Gruß Gunther

              1. Hallo Gunther,

                Während der Doppelpunkt im Headerfeldnamen nicht vorkommen darf, ist der Doppelpunkt im Body erlaubt.
                Der Punkt ist doch, dass zwischen dem field name und dem field body _immer_ ein Doppelpunkt stehen _muss_. Daher wäre für mich unter dem Sicherheitsaspekt die Eingabe eines Doppelpunkts seitens des Users im Formular ein Ausschlusskriterium.

                Warum? Er ist erlaubt, er ist gegebenenfalls sinnvoll. Warum ausschließen? Versteh' ich nicht. Entferne die Zeilenumbrüche und der Möchtegerncracker kann kein Headerfeld selbst erzeugen.

                Nein. Du willst also viele zulässige E-Mail-Adressen und Headerinhalte ausschließen. Das ist keine gute Idee.

                1. Wieso Headerinhalte? Die Benutzer _sollen_ ja wohl _keine_ Headerangaben selbst in das Formular eintragen.

                Äh selbstverständlich, darum geht es ja in diesem Thread. Sonst wäre Headerinjektion gar nicht möglich. Wenn Dein Formular keine Möglichkeit bietet, durch Headerinjektion eigene Header zu erzeugen, dann kann es natürlich auch keine Header erzeugen - klingt logisch.
                Und Headerinjektion ist das Erzeugen von vom Formularanbieter nicht gewollten Headerzeilen.

                1. Und ja, im local part einer Emailadresse wäre auch der Doppelpunkt erlaubt (allerdings müsste der local-part dann entweder in doppelte Anführungsstriche eingeschlossen werden oder der Doppelpunkt durch einen vorangestellten Backslash escaped werden).

                Und genau hier sehe ich einen gravierenden Unterschied zwischen Theorie und Praxis, bzw. den Sicherheitsanforderungen eines Webmasters.

                Und ja, ich sehe es durchaus als vertretbar an, wenn man Emailadressen, die einen Doppelpunkt enthalten, zurückweist (zumindest bei einem Kontaktformular)! Denn wie oft wird daran das Zutsandekommen einer Kommunikation in der Praxis wirklich scheitern? Kann man nicht eher davon ausgehen, dass User mit einer solchen Emailadresse (mit Doppelpunkt) auch noch mind. eine Alternative ohne haben?

                Sorry. Nein. Verstehe ich nicht. Habe ich kein Verständnis dafür. Wenn keine Sicherheitsprobleme vorhanden sind, dann ist es keine gute Idee, auch nur einen einzigen möglichen Nutzer auszuschließen. Es ist nicht notwendig. Warum also? Warum? Was hat es mit den Sicherheitsanforderungen zu tun?

                Verhindere Headerinjektion und gut ist.
                Verhindere keine richtigen Eingaben aus einem Bauchgefühl heraus, dass durch nichts begründbar ist. Der Doppelpunkt ist ok, durch Ausfiltern gewinnst Du meiner Meinung nach nichts dazu.

                Ich glaube schon, denn ansonsten dürften diese Leute eh des öfteren erhebliche Probleme haben, da es seit Jahren durchaus gängige Praxis im Web ist, Adressen in dieser Form zu überprüfen. Und da spielen die RFCs eben keine Rolle.

                Jaja, diese gängige Praxis verhindert alles Neue. Sich auf diese zu berufen, diese fortzuschreiben ist der größte Fehler, den es gibt - siehe IE6 und Webstandards.

                Diese gängige Praxis muss weg. Sei offen. Es gibt keinen Grund, sie fortzusetzen. Ganz im Gegenteil: Du weißt jetzt, dass diese Praxis beschränkt ist.

                Wie heißt der Grundsatz so schön:
                Sei liberal mit dem, was Du entgegennimmst.
                Sei konservativ mit dem, was Du versendest.

                Freundliche Grüße

                Vinzenz

                1. Hallo Vinzenz,

                  Entferne die Zeilenumbrüche und der Möchtegerncracker kann kein Headerfeld selbst erzeugen.

                  Verhindere Headerinjektion und gut ist.

                  also war und ist Roberts Ansatz im Ausgangsposting ja schon 100%ig sicher?

                  Wenn dem so ist (was ich, je mehr ich hier und anderswo lese, immer weniger verstehe), stimme ich dir natürlich zu, was den Ausschluss von erlaubten Formaten angeht.

                  Vor allem ist mir (scheinbar) die mail() von PHP nicht ganz klar. Laut Manual sind die Parameter wiefolgt:
                  bool mail ( string to, string subject, string message [, string additional_headers [, string additional_parameters]] )

                  Ich verwende bspw. ein Script (zum Glück ohne Benutzereingaben) in dem außer dem subject alle anderen Angaben in den additional_headers stehen.

                  Um also auf Nummer Sicher zu gehen, muss man ungewollte Headerangaben für alle Parameter ausschließen?

                  Gruß Gunther

                  1. Hello,

                    Um also auf Nummer Sicher zu gehen, muss man ungewollte Headerangaben für alle Parameter ausschließen?

                    Man muss Parameterinhalte (bzw. hier Argumentinhalte) ausschließen, die zu ungewollten Headerangaben führen können. Dafür kommen alle Funktionsargumente in Frage, die in die Header der Mail einzug halten, also die Standard-Header To und Subject, sowie die ergänzenden Header, die man in "additional_headers" angeben kann. On auch "additional_parameters" gefährdet ist, habe ich noch nicht genauer untersucht, möchte aber vorsichtig annehmen, dass auch hierüber unerwünschte Header eingeschleust werden könnten.

                    Nur das Funktionsargument "message" ist in Bezug auf die Header nicht gefährdet, da es den plain-text-Teil des Mail-Body darstellt.

                    Die Funktion mail() http://de.php.net/manual/de/function.mail.php ist mMn als extrem rudimentär zu bezeichnen. Sie sorgt weder für die Überprüfung der Header, noch sorgt sie für die passende Kodierung derselben. Sie stellt auch keine definierte Schnittstelle für den PHP-Anwendungs-Programmierer dar, da das jeweilige Mailsystem hinter der Mail-Funktion auf das Verhalten nochmals gevierenden Einfluß hat. "Mail()" kann also nur eine Arbeitshilfe sein.

                    Liebe Grüße

                    Tom

                    --
                    Coming Back soon

        2. Moin!

          Davon sollte man ausgehen. Außerdem lassen doch die Header für den Betreff und Absender auch nur eine Zeile als Wert zu, oder?
          Du denkst falsch - sprich in Zeilen. Headerdaten können Zeilenumbruch-Codes enthalten und dadurch weitere Headerzeilen einschmuggeln.

          Genau das will ich doch gerade verhindern, weshalb ChrisB auch vorgeschlagen hat, Betreff oder Absenderadresse mit Zeilenumbrüchen zu verwerfen.

          Meine Frage, die mich an dieser Stelle wirklich brennend interessiert: Reichen Zeilenumbrüche aus oder gibt es darüber noch andere gefährliche Zeichen, auf die man aufpassen sollte?

          Nach Toms Hinweis müsste auf jeden Fall noch eine Codierung des Betreffs und der Adresse (?) stattfinden, da als Wert nur 7-Bit-ASCII zugelassen ist.

          Und wenn ich meinen ursprünglichen Ansatz betrachte, dann sollten im Namen keine doppelten Quotes und in der Adresse keine spitzen Klammern auftauchen, oder?

          Viele Grüße,
          Robert

  2. Hello Robert,

      
    
    > function sendMail($fromName, $fromAddr, $toAddr, $subj, $text) {  
    >         /* Diese drei Werte koennen fuer Headerinjektionen benutzt  
    >          * und werden daher extra behandelt.  
    >          */  
    >         $subj   = str_replace(array("\n", "\r"), ' ', $subj);  
    >         $fromName = str_replace(array("\n", "\r"), ' ', $fromName);  
    >         $fromAddr = str_replace(array("\n", "\r"), '', $fromAddr);  
    >   
    >         return mail($toAddr, $subj, $text,  
    >                 "From: \"$fromName\" <$fromAddr>");  
    > }  
    
    

    Die Problematik ist eine ganz andere. Das wurde hier auch die letzten Tage schon verstreut diskutiert, soweit ich das selber mitbekommen habe. (Bin da im Moment noch extrem eingeschränkt)

    1. Es ist wesentlich, ob die eMail später auf einem Linux-Host durch ein "sendmail"-Script
       oder auf einem Windows-Host durch eine "echte" SMTP-Verbindung versendet wird.
       Andersherum würde entsprechendes gelten, habe ich aber selber noch nicht gesehen (also
       sendmail-Script auf einem Windows-Host oder MTA direkt ansprechen auf einem Linux)

    Das Sendmail-Script nimmt entsprechend seiner Entstehungsgeschichte die einfachen Zeilen-
       Umbrüche (chr(10)) und baut sie selber zu chr(13).chr(10) um. Wenn man da nun bereits 0D 0A
       reinstopfen würde, gäbe es doppelte Zeilenumbrüche, da sowohl 0D als auch 0A zu einer voll-
       ständigen Sequenz ergänzt werden würde. Beim direkten Versand über Port 25 findet keine
       Manipulation mehr statt.

    Du müsstest also für eine vernünftige Funktion erst einmal entscheiden, WIE der Versand
       stattfinden soll, also die Plattform bzw. Anbindung an den Mail-Transport-Agent festlegen.

    2. Extraktion von Zeilenumbrüchen aus den Headerzeilen siehe Einwände der Anderen
       Wenn welche drins sind, dann stimmt etwas nicht

    3. Kodierung für die Headerzeilen festlegen.
       In Headerzeilen dürfen nur 7-Bit-ASCII-Zeichen stehen. Also müssen diese kodiert werden.
       Das geschieht in der Praxis in zwei Stufen. Content-Encoding und Content-Transfer-Encoding.
       Das Content-Encoding ist für den Client später wichtig, da er sonst nicht wissen kann, in
       welcher Kodierung der Inhalt eigentlich vorliegt (z.B. ISO 8859-X oder UTF-8)
       Das transfer-Encoding ist für den Mailversand wichtig, da ja nur 7-Bit-ASCII versandt werden
       darf in den Headern. Dafür wird dann der bereits kodierte Content nochmals kodiert.
       üblich sind Quoted Printable (Q) und (seltener) Base64 (B).

    Hierzu hatte Chris© ein paar Muster rausgesucht und mich dadurch eigentlich erst darauf
       gebracht, dass ich hier selber auch noch Handlungsbedarf haben werde in meinen Scripten.
       https://forum.selfhtml.org/?t=168235&m=1097616

    Dass der übrige Inhalt ebenfalls diesen Kodierungsregeln unterliegt, versteht sich von
       selbst, aber da wird es ja auch explizit als Header zum Abschnitt angegeben, sollte also
       gar nicht vergessen werden können.

    Wenn Du nun also eine gute Hüllfunktion schreiben willst, dann solltest Du diese Dinge berücksichtigen. Hinzu kämen dann natürlich noch die Aufgabenstellungen der unterschiedlichen
    MIME-Mail-Typen... Da hast Du dann aber gelich eine neue Mail-Klasse als Aufgabe.

    Liebe Grüße

    Tom

    --
    Coming Back soon