Marc Reichelt: E-Mail-Validierung: Neuer Ansatz

Hallo an alle,

für mein aktuelles Projekt ipv6-fuer-alle.de benötige ich eine Funktion, die prüft, ob die Adresse für die PHP-mail()-Funktion genutzt werden kann.

Hierbei möchte ich ausdrücklich NICHT prüfen, ob die E-Mail-Adresse logisch korrekt ist, sondern größeren Schaden verhindern. Hierzu soll die Funktion folgenden Gesichtspunkten standhalten:

1. Sie soll so einfach wie möglich gestaltet sein.

2. Die Benutzung in der Funktion mail() soll möglich sein ohne Schaden anzurichten (Injenction).

3. Es soll nur eine E-Mail-Adresse angegeben werden können (nicht mehrere).

4. Die E-Mail-Adresse soll eindeutig identifizierbar sein, damit nicht mehrfach die gleiche Adresse als Ziel verwendet wird.

5. Nicht jede mögliche Form einer E-Mail-Adresse muss der Prüfung standhalten, wenn sie eine der ersten 4 Regeln verletzt.

Dazu habe ich mir nun folgende Gedanken gemacht:

  • Es muss mindestens ein "@" vorkommen.
  • Die Gesamtlänge darf 255 nicht überschreiten (willkürlich gewählt, um ein Limit zu setzen).
  • zu 2.) Die ersten 32 ASCII-Codes müssen ausgeschlossen werden.
  • zu 3.) Die Zeichen "," und ";" müssen ausgeschlossen werden.
  • zu 4.) Die Zeichen "<" und ">" müssen ausgeschlossen werden, sonst sind Mehrfachnennungen der Form "Eva Mustermann 123@example.com" und "Adam Mustermann 123@example.com" möglich.

Ich möchte mir diese Funktion bis morgen Abend geschrieben und auch getestet haben, werde diese aber natürlich dann auch hier veröffentlichen.

Meine Frage an euch ist nun: Fällt euch noch etwas dazu ein, das beachtet werden sollte?

Grüße

Marc Reichelt || http://www.marcreichelt.de/

--
DPRINTK("Last time you were disconnected, how about now?");
        linux-2.6.6/drivers/net/tokenring/ibmtr.c
Selfcode: ie:{ fl:| br:> va:} ls:< fo:} rl:( n4:( ss:) de:> js:| ch:? sh:| mo:) zu:)
  1. Moin Moin!

    Hierbei möchte ich ausdrücklich NICHT prüfen, ob die E-Mail-Adresse logisch korrekt ist, sondern größeren Schaden verhindern.

    Was für Schäden?

    Hierzu soll die Funktion folgenden Gesichtspunkten standhalten:

    1. Sie soll so einfach wie möglich gestaltet sein.

    ... aber nicht einfacher (Einstein)

    1. Die Benutzung in der Funktion mail() soll möglich sein ohne Schaden anzurichten (Injenction).

    Wenn PHP selbst nicht vernünftig escapen kann, mußt Du es vorher machen.

    1. Es soll nur eine E-Mail-Adresse angegeben werden können (nicht mehrere).
    1. Die E-Mail-Adresse soll eindeutig identifizierbar sein, damit nicht mehrfach die gleiche Adresse als Ziel verwendet wird.

    Welche der folgenden Adressen sind mit joe@example.com identisch? Welche füllen garantiert die selbe Mailbox?

    joe@example.com.
    Joe@example.com
    JOE@example.com
    JOE@EXAMPLE.COM
    joe@EXAMPLE.COM
    joe+ipv6@example.com
    joe-ipv6@example.com
    joe@ipv6.example.com
    joe@example.org
    josef-maria.doe@example.com

    • Es muss mindestens ein "@" vorkommen.
    • Nach dem letzten @ muß mindestens ein Punkt vorkommen, der nicht direkt am @ hängen darf.

    • Ein abschließender "." ist zwar selten, aber technisch korrekt.

    • Die Gesamtlänge darf 255 nicht überschreiten (willkürlich gewählt, um ein Limit zu setzen).

    Naja, gut, das kannst Du machen. Aber ein Limit ist in den RFCs nicht drin, soweit ich mich erinnere. Hostnamen haben eine Längenbegrenzung, Mail-Accounts nicht.

    • zu 2.) Die ersten 32 ASCII-Codes müssen ausgeschlossen werden.

    Tja, da könnte man bestimmt was aus den RFCs basteln, aber das geht wohl so in Ordnung.

    • zu 3.) Die Zeichen "," und ";" müssen ausgeschlossen werden.

    Prinzipiell könnte man auch diese beiden Zeichen im Account-Namen haben, aber das hat wohl kaum einer.

    • zu 4.) Die Zeichen "<" und ">" müssen ausgeschlossen werden, sonst sind Mehrfachnennungen der Form "Eva Mustermann 123@example.com" und "Adam Mustermann 123@example.com" möglich.

    s/^(.+)\s+<([^<>]+)>$/$2/;

    Alexander

    --
    Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
    1. Hello,

      • Die Gesamtlänge darf 255 nicht überschreiten (willkürlich gewählt, um ein Limit zu setzen).

      Naja, gut, das kannst Du machen. Aber ein Limit ist in den RFCs nicht drin, soweit ich mich erinnere. Hostnamen haben eine Längenbegrenzung, Mail-Accounts nicht.

      Aber Zeilenlängen in Mails sind begrenzt. Da ein Header nicht länger sein kann, als eine Zeile, ist er also auf die Zeilenlänge (minus Umbruch ) begrenzt.

      Die maximale Länge und die RFC dazu muss sich hier irgendwo im Archiv rumlümmeln.

      Liebe Grüße aus dem schönen Oberharz

      Tom vom Berg

      --
      Nur selber lernen macht schlau
      http://bergpost.annerschbarrich.de
      1. Hello,

        siehe:
        http://forum.de.selfhtml.org/archiv/2008/3/t168247/#m1098015

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
      2. Moin Moin!

        Hello,

        »» > - Die Gesamtlänge darf 255 nicht überschreiten (willkürlich gewählt, um ein Limit zu setzen).
        »»
        »» Naja, gut, das kannst Du machen. Aber ein Limit ist in den RFCs nicht drin, soweit ich mich erinnere. Hostnamen haben eine Längenbegrenzung, Mail-Accounts nicht.

        Aber Zeilenlängen in Mails sind begrenzt. Da ein Header nicht länger sein kann, als eine Zeile, ist er also auf die Zeilenlänge (minus Umbruch ) begrenzt.

        Doch, der Header kann durchaus länger als eine Zeile sein. Die folgenden Zeilen beginnen dann mit White Space und zählen logisch zur vorherigen Zeile. Das kannst Du sehr gut bei den Received-Headern fast jeder Mail sehen. Du kannst Dir auch einfach selbst eine Mail mit einem Subject von mehr als 80 Zeichen schicken und Dir dann den Subject-Header ansehen.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
        1. Hello,

          »» > - Die Gesamtlänge darf 255 nicht überschreiten (willkürlich gewählt, um ein Limit zu setzen).
          »»
          »» Naja, gut, das kannst Du machen. Aber ein Limit ist in den RFCs nicht drin, soweit ich mich erinnere. Hostnamen haben eine Längenbegrenzung, Mail-Accounts nicht.

          Aber Zeilenlängen in Mails sind begrenzt. Da ein Header nicht länger sein kann, als eine Zeile, ist er also auf die Zeilenlänge (minus Umbruch ) begrenzt.

          Doch, der Header kann durchaus länger als eine Zeile sein. Die folgenden Zeilen beginnen dann mit White Space und zählen logisch zur vorherigen Zeile. Das kannst Du sehr gut bei den Received-Headern fast jeder Mail sehen. Du kannst Dir auch einfach selbst eine Mail mit einem Subject von mehr als 80 Zeichen schicken und Dir dann den Subject-Header ansehen.

          Die maximale Zeilenlänge laut RFC beträgt 1000 Zeichen inclusive Zeilenendezeichen.

          Wo steht das denn mit den mehrzeiligen Headern?

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
          Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
          1. Moin Moin!

            Die maximale Zeilenlänge laut RFC beträgt 1000 Zeichen inclusive Zeilenendezeichen.

            Richtig. RFC 2822 Abschnitt 2.1.1 sagt, das keine einzelne Zeile länger als 998 Zeichen + CRLF sein darf ("MUST NOT"), gleichzeitig sollten Empfänger aber keine Limits bei der Verarbeitung haben und wenigstens 998 Zeichen+CRLF verdauen können.

            Wo steht das denn mit den mehrzeiligen Headern?

            RFC 2822 Abschnitt 2.2.3:

            Each header field is logically a single line of characters comprising
               the field name, the colon, and the field body.  For convenience
               however, and to deal with the 998/78 character limitations per line,
               the field body portion of a header field can be split into a multiple
               line representation; this is called "folding".  The general rule is
               that wherever this standard allows for folding white space (not
               simply WSP characters), a CRLF may be inserted before any WSP.

            Eine einzelne phsyische Zeile in einer standardkonformen E-Mail kann also nie länger als 998 Zeichen+CRLF sein. Ein einzelner Header kann durch Folding und Unfolding aber durchaus wesentlich länger als 1000 Zeichen sein.

            Alexander

            --
            Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
            1. Hello,

              Eine einzelne phsyische Zeile in einer standardkonformen E-Mail kann also nie länger als 998 Zeichen+CRLF sein. Ein einzelner Header kann durch Folding und Unfolding aber durchaus wesentlich länger als 1000 Zeichen sein.

              Das verstehe ich anders. Steht auch an einer anderen Stelle nochmal anders. Muss ich aber selber suchen:

              Eine einzelne Zeile kann maximal 998+CRLF werden.
              Bei Überschreitung der 78 Zeichen (für die Lesbarkeit, Convenience) kann gefaltet werden.

              Das ist noch von Jonathan und mMn noch gültig, weil in RFC 822 und RFC 1341 nochmals kompatibel wiederholt. Das habe ich aber nur noch so im Gedächtnis und noch nicht wieder neu rausgesucht:

              4.5.3.  SIZES

              There are several objects that have required minimum maximum
                       sizes.  That is, every implementation must be able to receive
                       objects of at least these sizes, but must not send objects
                       larger than these sizes.

              ****************************************************
                        *                                                  *
                        *  TO THE MAXIMUM EXTENT POSSIBLE, IMPLEMENTATION  *
                        *  TECHNIQUES WHICH IMPOSE NO LIMITS ON THE LENGTH *
                        *  OF THESE OBJECTS SHOULD BE USED.                *
                        *                                                  *
                        ****************************************************

              user

              The maximum total length of a user name is 64 characters.

              domain

              The maximum total length of a domain name or number is 64
                             characters.

              path

              The maximum total length of a reverse-path or
                             forward-path is 256 characters (including the punctuation
                             and element separators).

              command line

              The maximum total length of a command line including the
                             command word and the <CRLF> is 512 characters.

              reply line

              The maximum total length of a reply line including the
                             reply code and the <CRLF> is 512 characters.

              [Page 42]                                                         Postel

              RFC 821                                                      August 1982
                                                         Simple Mail Transfer Protocol

              text line

              The maximum total length of a text line including the
                             <CRLF> is 1000 characters (but not counting the leading
                             dot duplicated for transparency).

              recipients buffer

              The maximum total number of recipients that must be
                             buffered is 100 recipients.

              ****************************************************
                        *                                                  *
                        *  TO THE MAXIMUM EXTENT POSSIBLE, IMPLEMENTATION  *
                        *  TECHNIQUES WHICH IMPOSE NO LIMITS ON THE LENGTH *
                        *  OF THESE OBJECTS SHOULD BE USED.                *
                        *                                                  *
                        ****************************************************

              Errors due to exceeding these limits may be reported by using
                       the reply codes, for example:

              500 Line too long.

              501 Path too long

              552 Too many recipients.

              552 Too much mail data.

              Liebe Grüße aus dem schönen Oberharz

              Tom vom Berg

              --
              Nur selber lernen macht schlau
              http://bergpost.annerschbarrich.de
              1. Moin Moin!

                Hello,

                »» Eine einzelne phsyische Zeile in einer standardkonformen E-Mail kann also nie länger als 998 Zeichen+CRLF sein. Ein einzelner Header kann durch Folding und Unfolding aber durchaus wesentlich länger als 1000 Zeichen sein.

                Das verstehe ich anders. Steht auch an einer anderen Stelle nochmal anders. Muss ich aber selber suchen:

                Eine einzelne Zeile kann maximal 998+CRLF werden.
                Bei Überschreitung der 78 Zeichen (für die Lesbarkeit, Convenience) kann gefaltet werden.

                Es kann jederzeit gefaltet werden. Das Falten erlaubt dem Field Body eine beliebige Länge. Field Bodies sind eine Abstraktionsebene oberhalb der physischen Zeilen, für ihre Representation in einem E-Mail-Header nach RFC2822 können so viele Zeilen wie nötig benutzt werden, eine Einschränkung der Zeilenzahl sehe ich nicht.

                Das ist noch von Jonathan und mMn noch gültig, weil in RFC 822

                obsoleted by RFC 2822

                und RFC 1341 nochmals kompatibel wiederholt.

                Darin finde ich gar keine Beschränkungen von Header-Zeilenlängen.

                Das habe ich aber nur noch so im Gedächtnis und noch nicht wieder neu rausgesucht:

                4.5.3.  SIZES

                There are several objects that have required minimum maximum
                         sizes.  That is, every implementation must be able to receive
                         objects of at least these sizes, but must not send objects
                         larger than these sizes.

                ****************************************************
                          *                                                  *
                          *  TO THE MAXIMUM EXTENT POSSIBLE, IMPLEMENTATION  *
                          *  TECHNIQUES WHICH IMPOSE NO LIMITS ON THE LENGTH *
                          *  OF THESE OBJECTS SHOULD BE USED.                *
                          *                                                  *
                          ****************************************************

                user

                The maximum total length of a user name is 64 characters.

                domain

                The maximum total length of a domain name or number is 64
                               characters.

                path

                The maximum total length of a reverse-path or
                               forward-path is 256 characters (including the punctuation
                               and element separators).

                command line

                The maximum total length of a command line including the
                               command word and the <CRLF> is 512 characters.

                reply line

                The maximum total length of a reply line including the
                               reply code and the <CRLF> is 512 characters.

                [Page 42]                                                         Postel

                RFC 821                                                      August 1982
                                                           Simple Mail Transfer Protocol

                text line

                The maximum total length of a text line including the
                               <CRLF> is 1000 characters (but not counting the leading
                               dot duplicated for transparency).

                recipients buffer

                The maximum total number of recipients that must be
                               buffered is 100 recipients.

                ****************************************************
                          *                                                  *
                          *  TO THE MAXIMUM EXTENT POSSIBLE, IMPLEMENTATION  *
                          *  TECHNIQUES WHICH IMPOSE NO LIMITS ON THE LENGTH *
                          *  OF THESE OBJECTS SHOULD BE USED.                *
                          *                                                  *
                          ****************************************************

                Errors due to exceeding these limits may be reported by using
                         the reply codes, for example:

                500 Line too long.

                501 Path too long

                552 Too many recipients.

                552 Too much mail data.

                Das beschränkt Zeilenlängen und Teile von Header-Feldwerten (Field bodies). 100 Empfänger mit je 64 Zeichen für den User, je einem @ und je 64 für die Domain plus einem Komma dazwischen macht 13.000 Zeichen. Wären Header-Felder auf 998 Zeichen beschränkt, müßte bei 100 Empfängern die durchschnittliche Länge einer Adresse unter 9,9 Zeichen liegen.

                Alexander

                --
                Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
    2. Hallo Alexander,

      »» Hierbei möchte ich ausdrücklich NICHT prüfen, ob die E-Mail-Adresse logisch korrekt ist, sondern größeren Schaden verhindern.

      Was für Schäden?

      Schäden der Form, wie ich sie beschrieben habe: Header Injections, Angabe mehrerer E-Mails auf ein Mal und Angabe mehrerer Strings, die alle eigentlich die selbe E-Mail-Adresse sind.

      »» 2. Die Benutzung in der Funktion mail() soll möglich sein ohne Schaden anzurichten (Injenction).

      Wenn PHP selbst nicht vernünftig escapen kann, mußt Du es vorher machen.

      Eben genau das ist mein Vorhaben.

      »» 4. Die E-Mail-Adresse soll eindeutig identifizierbar sein, damit nicht mehrfach die gleiche Adresse als Ziel verwendet wird.

      Welche der folgenden Adressen sind mit joe@example.com identisch? Welche füllen garantiert die selbe Mailbox?

      joe@example.com.

      Das ist in der Tat eine interessante Angabe - eine einfache Regel hierfür wäre das Ausschließen von Punkten am Ende.

      Joe@example.com
      JOE@example.com
      JOE@EXAMPLE.COM
      joe@EXAMPLE.COM

      Dies hätte mich sogar gar nicht betroffen, da ich MySQL zur Abfrage verwende. Dennoch ist dies ein wichtiger Punkt, falls Groß- und Kleinschreibung unterschieden wird.

      joe+ipv6@example.com
      joe-ipv6@example.com
      joe@ipv6.example.com
      joe@example.org
      josef-maria.doe@example.com

      Soweit ich das sehe sind dies alles unterschiedliche Adressen, die eventuell später auf die gleiche Mailbox verwaisen können - so etwas soll die Funktion nicht prüfen können. Und außerdem ist das praktisch nicht möglich.

      »» - Es muss mindestens ein "@" vorkommen.

      • Nach dem letzten @ muß mindestens ein Punkt vorkommen, der nicht direkt am @ hängen darf.

      Gute Regel - kann direkt aufgenommen werden, allerdings fehlt mir hier die Angriffsmöglichkeit.

      • Ein abschließender "." ist zwar selten, aber technisch korrekt.

      Eben deshalb könnte ich ihn in meiner E-Mail-Validierung ausschließen.

      »» - Die Gesamtlänge darf 255 nicht überschreiten (willkürlich gewählt, um ein Limit zu setzen).

      Naja, gut, das kannst Du machen. Aber ein Limit ist in den RFCs nicht drin, soweit ich mich erinnere. Hostnamen haben eine Längenbegrenzung, Mail-Accounts nicht.

      Meine Tabellenspalte in der Datenbank aber schon. ;)
      Und: Solche langen E-Mail-Adressen können allein schon deshalb ausgeschlossen werden, weil sie so selten vorkommen. Und wenn sie vorkommen, ist das in 99,9% der Fälle Absicht.

      »» - zu 4.) Die Zeichen "<" und ">" müssen ausgeschlossen werden, sonst sind Mehrfachnennungen der Form "Eva Mustermann 123@example.com" und "Adam Mustermann 123@example.com" möglich.

      s/^(.+)\s+<([^<>]+)>$/$2/;

      Ich könnte natürlich auch einfach das Vorkommen jeglicher "<" und ">" in der Adresse verbieten - das würde meine Regel (1) erfüllen. So ein regulärer Ausdruck ist zwar auch ganz schick und schnell geschrieben, jedoch später schwer nachzuvollziehen wenn man sich länger nicht damit beschäftigt hat.

      Alles in allem hast du noch einige Dinge gefunden, die ich in meine Funktion mit aufnehmen sollte. Sehr gut!

      Grüße

      Marc Reichelt || http://www.marcreichelt.de/

      --
      DPRINTK("Last time you were disconnected, how about now?");
              linux-2.6.6/drivers/net/tokenring/ibmtr.c
      Selfcode: ie:{ fl:| br:> va:} ls:< fo:} rl:( n4:( ss:) de:> js:| ch:? sh:| mo:) zu:)
      1. Moin Moin!

        Hallo Alexander,

        »» »» Hierbei möchte ich ausdrücklich NICHT prüfen, ob die E-Mail-Adresse logisch korrekt ist, sondern größeren Schaden verhindern.
        »»
        »» Was für Schäden?

        Schäden der Form, wie ich sie beschrieben habe: Header Injections, Angabe mehrerer E-Mails auf ein Mal und Angabe mehrerer Strings, die alle eigentlich die selbe E-Mail-Adresse sind.

        »» »» 2. Die Benutzung in der Funktion mail() soll möglich sein ohne Schaden anzurichten (Injenction).
        »»
        »» Wenn PHP selbst nicht vernünftig escapen kann, mußt Du es vorher machen.

        Eben genau das ist mein Vorhaben.

        Schade, dass PHP sich nicht um so essenzielle Sachen kümmert.

        »» »» 4. Die E-Mail-Adresse soll eindeutig identifizierbar sein, damit nicht mehrfach die gleiche Adresse als Ziel verwendet wird.
        »»
        »» Welche der folgenden Adressen sind mit joe@example.com identisch? Welche füllen garantiert die selbe Mailbox?
        »»
        »» joe@example.com.

        Das ist in der Tat eine interessante Angabe - eine einfache Regel hierfür wäre das Ausschließen von Punkten am Ende.

        »» Joe@example.com
        »» JOE@example.com
        »» JOE@EXAMPLE.COM
        »» joe@EXAMPLE.COM

        Dies hätte mich sogar gar nicht betroffen, da ich MySQL zur Abfrage verwende. Dennoch ist dies ein wichtiger Punkt, falls Groß- und Kleinschreibung unterschieden wird.

        Rechts des letzten @ nicht, links schon. Typische Mail-Provider arbeiten case insensitiv, aber die RFCs erlauben case sensitive Account-Namen. Joe@example.com, JOE@EXAMPLE.COM und joe@example.com sind bis zu drei verschiedene Mailboxen. joe@example.com und joe@EXAMPLE.COM sind dagegen identisch, ebenso JOE@EXAMPLE.COM und JOE@example.com.

        »» joe+ipv6@example.com
        »» joe-ipv6@example.com
        »» joe@ipv6.example.com
        »» joe@example.org
        »» josef-maria.doe@example.com

        Soweit ich das sehe sind dies alles unterschiedliche Adressen, die eventuell später auf die gleiche Mailbox verwaisen können - so etwas soll die Funktion nicht prüfen können. Und außerdem ist das praktisch nicht möglich.

        Richtig.

        »» »» - Es muss mindestens ein "@" vorkommen.
        »»
        »» - Nach dem letzten @ muß mindestens ein Punkt vorkommen, der nicht direkt am @ hängen darf.

        Gute Regel - kann direkt aufgenommen werden, allerdings fehlt mir hier die Angriffsmöglichkeit.

        joe@.example.com ist einfach keine gültige Mail-Adresse. Also gibt es auch keinen Grund, die weiter zu behandeln.

        »» - Ein abschließender "." ist zwar selten, aber technisch korrekt.

        Eben deshalb könnte ich ihn in meiner E-Mail-Validierung ausschließen.

        Nein. s/.$//;

        »» »» - Die Gesamtlänge darf 255 nicht überschreiten (willkürlich gewählt, um ein Limit zu setzen).
        »»
        »» Naja, gut, das kannst Du machen. Aber ein Limit ist in den RFCs nicht drin, soweit ich mich erinnere. Hostnamen haben eine Längenbegrenzung, Mail-Accounts nicht.

        Meine Tabellenspalte in der Datenbank aber schon. ;)

        Nimm text, dann brauchst Du Dir darum keine Sorgen mehr machen. ;-)

        Und: Solche langen E-Mail-Adressen können allein schon deshalb ausgeschlossen werden, weil sie so selten vorkommen. Und wenn sie vorkommen, ist das in 99,9% der Fälle Absicht.

        Naja, wer mit einer mehr als 80 Zeichen langen E-Mail-adresse geschlagen ist, wird sich wohl irgendwo eine kürzere besorgen.

        Was aber durchaus vorkommen kann, sind viele Punkte rechts des letzten @. Ich hatte mal eine Adresse xxxxx@hermes.et-inf.xxxxxx.xxx. Auch können solche Adressen bei .uk und anderen TLDs mit fixen SLDs vorkommen: joe@stud.cs.u-bla.edu.tld

        Möglich (aber zugegeben extrem selten) wären auch IP-Adressen: joe@127.128.129.130

        »» »» - zu 4.) Die Zeichen "<" und ">" müssen ausgeschlossen werden, sonst sind Mehrfachnennungen der Form "Eva Mustermann 123@example.com" und "Adam Mustermann 123@example.com" möglich.
        »»
        »» s/^(.+)\s+<([^<>]+)>$/$2/;

        Ich könnte natürlich auch einfach das Vorkommen jeglicher "<" und ">" in der Adresse verbieten - das würde meine Regel (1) erfüllen. So ein regulärer Ausdruck ist zwar auch ganz schick und schnell geschrieben, jedoch später schwer nachzuvollziehen wenn man sich länger nicht damit beschäftigt hat.

        OK, laß mich die Codezeile verbessern:
        s/^(.+)\s+<([^<>]+)>$/$2/; # extract e-mail address from "name e-mail@address"

        CPAN hat dafür übrigens zwei fertige Module: Email::Valid zum Testen und Email::Address zum Parsen.

        Alexander

        --
        Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
        1. Hello,

          »» Wenn PHP selbst nicht vernünftig escapen kann, mußt Du es vorher machen.

          Eben genau das ist mein Vorhaben.

          Schade, dass PHP sich nicht um so essenzielle Sachen kümmert.

          Die lassen uns das hier erst ausdiskutieren, bis sich eine Konvergenz erkennen lässt, diskutieren es dann in dieser Richtung zuende und bauen es dann erst ein. Das kann also dauern :-)

          Liebe Grüße aus dem schönen Oberharz

          Tom vom Berg

          --
          Nur selber lernen macht schlau
          http://bergpost.annerschbarrich.de
    3. Hallo,

      »» 1. Sie soll so einfach wie möglich gestaltet sein.
      ... aber nicht einfacher (Einstein)

      das klingt tatsächlich nach Einstein. Gefällt mir! :-)

      »» 4. Die E-Mail-Adresse soll eindeutig identifizierbar sein, damit nicht mehrfach die gleiche Adresse als Ziel verwendet wird.

      Welche der folgenden Adressen sind mit joe@example.com identisch? Welche füllen garantiert die selbe Mailbox?

      Da ein- und dieselbe Mailbox auch noch beliebig viele Aliases haben kann, ist hier bestenfalls eine Positivaussage "Adressen sind gleichwertig" denkbar. Die umgekehrte Aussage "Adressen gehören zu verschiedenen Postfächern" ist prinzipiell nicht möglich.

      joe@example.com.
      Joe@example.com
      JOE@example.com
      JOE@EXAMPLE.COM
      joe@EXAMPLE.COM

      Ich finde keine Stelle in FRC 2822, die etwas darüber aussagt, ob der Local Part nun case-sensitive ist; ich kenne nur die Praxis vieler Provider, den Local Part *nicht* case-sensitive zu betrachten. Damit sind joe@example.com und JOE@example.com gleichwertig.
      Der Domain Part ist sowieso case-insensitive.

      • Nach dem letzten @ muß mindestens ein Punkt vorkommen, der nicht direkt am @ hängen darf.

      Warum? Ist joe@localhost etwa keine gültige Mailadresse? - Zugegeben, keine die man in freier Wildbahn erwarten würde.

      »» - zu 4.) Die Zeichen "<" und ">" müssen ausgeschlossen werden, sonst sind Mehrfachnennungen der Form "Eva Mustermann 123@example.com" und "Adam Mustermann 123@example.com" möglich.

      Obacht: Bei der Notation "Klartextname <mailadresse>" muss man natürlich den ganzen Kram außerhalb der <> von technischen Plausibilitätsprüfungen ausnehmen, aber trotzdem auf Unerlaubtes (Zeilenumbrüche->Header Injection) testen.
      Wenn man < und > allerdings ganz ausschließt ...

      So long,
       Martin

      --
      Wer im Steinhaus sitzt, soll nicht mit Gläsern werfen.
      1. Hello,

        Warum? Ist joe@localhost etwa keine gültige Mailadresse? - Zugegeben, keine die man in freier Wildbahn erwarten würde.

        Wenn Du das so handhabst, dann ist 'joe' alleine auch gültig.
        In der lokalen Zustellung wird bei allen mir bekannten Mailservern immer erst nach einem lokalen Konto geschaut. Da ist überhaupt keine Domain notwendig

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
  2. echo $begrüßung;

    für mein aktuelles Projekt ipv6-fuer-alle.de benötige ich eine Funktion, die prüft, ob die Adresse für die PHP-mail()-Funktion genutzt werden kann.
    Meine Frage an euch ist nun: Fällt euch noch etwas dazu ein, das beachtet werden sollte?

    Mir ist nicht bekannt, dass mail() mit Nicht-ASCII-Zeichen umgehen und diese in Punycode wandeln könnte. Wenn dein SMTP-Server das auch nicht kann, müsstest du diese Verwendung ebenfalls unterbinden oder selbst eingreifen.

    echo "$verabschiedung $name";

    1. Hallo dedlfix,

      Mir ist nicht bekannt, dass mail() mit Nicht-ASCII-Zeichen umgehen und diese in Punycode wandeln könnte. Wenn dein SMTP-Server das auch nicht kann, müsstest du diese Verwendung ebenfalls unterbinden oder selbst eingreifen.

      Das klingt logisch. Besteht hier eine Angriffsmöglichkeit? Wenn ja: Gibt es eine einfache Regel, um dies zu vermeiden? Das Verbieten aller Zeichencodes über 127 wird allerdings wohl auch dazu führen, dass E-Mail-Adressen der Form "mäxchen@example.com" nicht funktionieren - und die würde ich ungerne ausschließen wollen.

      Wenn diese Regel eingefügt wird sieht meine Funktion übrigens derzeit wie folgt aus:

      function testMail($str) {  
      	if (!is_string($str) || strlen($str) > 255) {  
      		// variable must be a string with a max. length of 255  
      		return false;  
      	} else if (strpos($str, '@') === false) {  
      		// there must be an @  
      		return false;  
      	} else {  
      		// check for invalid characters  
      		for ($i = 0; $i < strlen($str); $i++) {  
      			$c = $str[$i];  
      			$cCode = ord($c);  
      			  
      			if ($cCode < 32) {  
      				// control characters not allowed  
      				return false;  
      			}  
      			  
      			if (strpos('<>,;', $c) !== FALSE) {  
      				// invalid characters  
      				return false;  
      			}  
      			  
      			if ($cCode > 127) {  
      				// non-ascii characters not allowed  
      				return false;  
      			}  
      		}  
      	}  
      	  
      	return true;  
      }
      

      Grüße

      Marc Reichelt || http://www.marcreichelt.de/

      --
      DPRINTK("Last time you were disconnected, how about now?");
              linux-2.6.6/drivers/net/tokenring/ibmtr.c
      Selfcode: ie:{ fl:| br:> va:} ls:< fo:} rl:( n4:( ss:) de:> js:| ch:? sh:| mo:) zu:)
      1. Hello,

        Das klingt logisch. Besteht hier eine Angriffsmöglichkeit?

        Woraus könnten denn Angriffe bestehen?

        • Dass unter einem falschen Mailaccount gemailt wird.
            Da hilft nur sasl oder ähnliches.

        • Dass eine Liste mit Empfängern eingeschleust wird und ein eigener Mailbody
            Der ordentliche wird abgewürgt durch vergurktes Multipart/related oder ähnliches,
            geht also mit, wird aber vom Client nicht mehr angezeigt...

        • ??

        Eingänge sind bei der PHP-Mail()-Funktion

        • to
        • subject
        • additional Headers

        Liebe Grüße aus dem schönen Oberharz

        Tom vom Berg

        --
        Nur selber lernen macht schlau
        http://bergpost.annerschbarrich.de
      2. echo $begrüßung;

        » [Nicht-ASCII-Zeichen ... Punycode ... SMTP-Server]
        Besteht hier eine Angriffsmöglichkeit?

        Ist mir nichts bekannt, was aber nichts heißt, denn das Thema kommt nur passiv bei mir vorbei. Allerdings bekommst du dadurch unerreichbare Mailadressen, was Bounces und "Ich bekomme keine Mail"-Probleme nach sich ziehen wird.

        Gibt es eine einfache Regel, um dies zu vermeiden? Das Verbieten aller Zeichencodes über 127 wird allerdings wohl auch dazu führen, dass E-Mail-Adressen der Form "mäxchen@example.com" nicht funktionieren - und die würde ich ungerne ausschließen wollen.

        Du musst ja nur die Zeichen nach dem @ prüfen.

        Wenn diese Regel eingefügt wird sieht meine Funktion übrigens derzeit wie folgt aus:

        Hast du mal einen Performance-Vergleich mit einem Regulären Ausdruck gemacht? Der sollte sich exklusive der is_string()-Prüfung als Einzeiler notieren lassen.

        echo "$verabschiedung $name";

  3. Hallo nochmals,

    vielen Dank für alle Antworten!

    Meine Funktion zum Prüfen auf ungültige Zeichen sieht derzeit wie folgt aus:

    function testMail($str) {  
    	if (!is_string($str) || strlen($str) > 255) {  
    		// variable must be a string with a max. length of 255  
    		return false;  
    	} else if (strpos($str, '@') === false) {  
    		// there must be an @  
    		return false;  
    	} else {  
    		// check for invalid characters  
    		for ($i = 0; $i < strlen($str); $i++) {  
    			$c = $str[$i];  
    			$cCode = ord($c);  
    			  
    			if ($cCode < 32 || $cCode > 127) {  
    				// control characters and non-ascii characters not allowed  
    				return false;  
    			}  
    			  
    			if (strpos('<>,; :\'"', $c) !== FALSE) {  
    				// invalid characters  
    				return false;  
    			}  
    			  
    			if ($i == (strlen($str) - 1) && $c == '.') {  
    				// last character should be no dot  
    				return false;  
    			}  
    		}  
    	}  
    	  
    	return true;  
    }
    

    Ich habe zu den anderen Zeichen noch die drei Zeichen : ' und " mit in die Liste der verbotenen Zeichen eingefügt - da diese in der Praxis kaum vorkommen.

    Alles in allem zeigt auch dieser Thread, dass die Syntax von E-Mail-Adressen ziemlich kompliziert ist und aufmerksam vor Verwendung in der mail()-Funktion geprüft werden sollte.

    WICHTIG (für's Archiv): Wer diese Funktion nutzt, sollte sich darüber im Klaren sein, dass dies keine Funktion ist um zu prüfen ob eine E-Mail-Adresse valide ist, sondern lediglich eine einigermaßen akzeptable Prüfung für die Verwendung in der mail()-Funktion ist.

    Grüße

    Marc Reichelt || http://www.marcreichelt.de/

    --
    DPRINTK("Last time you were disconnected, how about now?");
            linux-2.6.6/drivers/net/tokenring/ibmtr.c
    Selfcode: ie:{ fl:| br:> va:} ls:< fo:} rl:( n4:( ss:) de:> js:| ch:? sh:| mo:) zu:)
    1. Moin Moin!

        // check for invalid characters  
        for ($i = 0; $i < strlen($str); $i++) {  
        	$c = $str[$i];  
        	$cCode = ord($c);  
        	  
        	if ($cCode < 32 || $cCode > 127) {  
        		// control characters and non-ascii characters not allowed  
        		return false;  
        	}  
        	  
        	if (strpos('<>,; :\'"', $c) !== FALSE) {  
        		// invalid characters  
        		return false;  
        	}  
        	  
        	if ($i == (strlen($str) - 1) && $c == '.') {  
        		// last character should be no dot  
        		return false;  
        	}  
      
        
      Das Stückchen Code sieht für mich fürchterlich ineffizient aus.  
        
      1\. Die Prüfung auf den Punkt am Ende würde ich vor die Schleife ziehen, das spart für jedes einzelne Zeichen in $str einen Aufruf von strlen.  
        
      ~~~php
        
      if ($str[strlen($str)-1]=='.') {  
        return false; // Alternativ: $str um ein Zeichen verkürzen, denn der Punkt ist KORREKT, nur ungewöhnlich  
      }  
      
      

      Das sieht so fürchterlich nach C aus:

        
      if (str[strlen(c)-1]=='.') {  
        return 0;  
        /* alternativ: */  
        str[strlen(c)-1]=0;  
      }  
      
      

      Kann PHP das nicht einfacher? Perl kann direkt auf das letzte Zeichen zugreifen, ohne strlen zu bemühen:

        
      if (substr($str,-1) eq '.') {  
        return;  
        # oder:  
        substr($str,-1)='';  
      }  
      # oder gleich per RE:  
      $str=~s/\.$//;  
      
      

      2. Ich würde nicht jedes einzelne Zeichen in einer interpretierten Schleife darauf hin untersuchen, ob es legal oder illegal ist. Vor allem würde ich es mir verkneifen, für jedes einzelne Zeichen in $str nochmal strpos() aufzurufen, das eine weitere Schleife darstellt.

      Du prüfst hier effektiv, ob $str ausschließlich aus einem vorgegebenen Satz von Zeichen besteht. Das ist ein Fall für die schnelle Regular Expression Engine.

      Die Blacklist-Version in Perl (analog in PHP nachbaubar):

        
      if ($str=~/[^\x00-\x20<>,;:'"\\\x7F-\xFF]/) {  
        # Explizit verboten:  
        # * ASCII-Steuerzeichen von 0x00=NUL bis einschließlich 0x20=Space  
        # * <>,;.'"\  
        # * ASCII-Zeichen ab 0x7F=DEL  
        return;  
      }  
      
      

      Ein einziges böses Zeichen bricht das Matching ab und rennt ins return.

      Haken: $str muß Bytes enthalten, keine UTF-8-Characters.
      Blacklist mit UTF-8:

        
      if ($str=~/[^\x00-\x20<>,;:'"\\\x7F-\x{10FFFF}]/) {  
        # Explizit verboten:  
        # * ASCII-Steuerzeichen von 0x00=NUL bis einschließlich 0x20=Space  
        # * <>,;.'"\  
        # * ASCII-Zeichen ab 0x7F=DEL einschließlich aller Unicode-Planes  
        return;  
      }  
      
      

      Whitelist-Version in Perl, mit etwas weniger erlaubten Zeichen (aus Tippfaulheit):

        
      # Funktioniert mit Bytes und Characters.  
      unless ($str=~/^[\@0-9A-Za-z._-]+$/) {  
        # Explizit erlaubt:  
        # * @ -- weil die gesamte Adresse gematcht wird  
        # * Ziffern, Buchstaben, und ausgesuchte Sonderzeichen (Minus muß aus technischen Gründen ganz hinten stehen)  
        # Der gesamte String muß aus diesen Zeichen bestehen. Die RE-Engine bricht beim ersten Mismatch ab, der Code rennt ins return.  
        return;  
      }  
      
      

      Ich würde die Whitelist-Version vorziehen, weil Du dann 100% garantieren kannst, welche Zeichen an die nicht escapende mail()-Funktion gehen.

      Alexander

      --
      Today I will gladly share my knowledge and experience, for there are no sweeter words than "I told you so".
      1. Hallo Alexander,

        Das Stückchen Code sieht für mich fürchterlich ineffizient aus.

        Er ist nicht sonderlich effizient, das stimmt - aber diese Funktion wird in meinem Projekt nicht so häufig aufgerufen, da kosten andere Dinge wesentlich mehr Laufzeit.
        Nichtsdestotrotz kann - und sollte man - hier noch nachbessern. Dies kann ja im Verlauf der nächsten Tage geschehen, solange der Thread noch nicht archiviert ist.

        1. Die Prüfung auf den Punkt am Ende würde ich vor die Schleife ziehen, das spart für jedes einzelne Zeichen in $str einen Aufruf von strlen.

        if ($str[strlen($str)-1]=='.') {
          return false; // Alternativ: $str um ein Zeichen verkürzen, denn der Punkt ist KORREKT, nur ungewöhnlich
        }

          
        Jepp, hier sollte man nur aufpassen dass $str auch wirklich eine Länge > 0 hat.  
          
        
        > Kann PHP das nicht einfacher? Perl kann direkt auf das letzte Zeichen zugreifen, ohne strlen zu bemühen:  
        >   
        > ~~~perl
          
        
        > if (substr($str,-1) eq '.') {  
        >   return;  
        >   # oder:  
        >   substr($str,-1)='';  
        > }  
        > # oder gleich per RE:  
        > $str=~s/\.$//;  
        > 
        
        

        Bei PHP geht's ziemlich ähnlich, kann man also auch abändern.

        1. Ich würde nicht jedes einzelne Zeichen in einer interpretierten Schleife darauf hin untersuchen, ob es legal oder illegal ist. Vor allem würde ich es mir verkneifen, für jedes einzelne Zeichen in $str nochmal strpos() aufzurufen, das eine weitere Schleife darstellt.

        Die Schleife mit strpos() ermittelt für jedes Zeichen $needle, ob es im illegalen Satz $haystack auftaucht. Das ist soweit ok - natürlich kann man hier ebenfalls tunen, aber es wird dem Aufwand bei mir nicht gerecht.

        Zu den regulären Ausdrücken: Sicher kann man das alles mit einem Ausdruck lösen, aber den versteht man später nicht mehr so leicht.

        Whitelist-Version in Perl, mit etwas weniger erlaubten Zeichen (aus Tippfaulheit):

        Funktioniert mit Bytes und Characters.

        unless ($str=~/[1]+$/) {
          # Explizit erlaubt:
          # * @ -- weil die gesamte Adresse gematcht wird
          # * Ziffern, Buchstaben, und ausgesuchte Sonderzeichen (Minus muß aus technischen Gründen ganz hinten stehen)
          # Der gesamte String muß aus diesen Zeichen bestehen. Die RE-Engine bricht beim ersten Mismatch ab, der Code rennt ins return.
          return;
        }

        
        >   
        > Ich würde die Whitelist-Version vorziehen, weil Du dann 100% garantieren kannst, welche Zeichen an die nicht escapende mail()-Funktion gehen.  
          
        Eine solche Whitelist hatte ich bislang verwendet - die mochte Vinzenz aber nicht. ;)  
          
          
        Grüße  
          
        Marc Reichelt || <http://www.marcreichelt.de/>  
        
        -- 
        DPRINTK("Last time you were disconnected, how about now?");  
                linux-2.6.6/drivers/net/tokenring/ibmtr.c  
          
        [Selfcode](http://emmanuel.dammerer.at/selfcode.html): ie:{ fl:| br:> va:} ls:< fo:} rl:( n4:( ss:) de:> js:| ch:? sh:| mo:) zu:)
        

        1. @0-9A-Za-z._- ↩︎