PHPGangsta - Der praktische PHP Blog

PHP Blog von PHPGangsta


Das SMTP Protokoll im Detail betrachtet

with 3 comments

Vor einem Jahr habe ich bereits das IMAP-Protokoll näher betrachtet, nun soll das SMTP Protokoll folgen. Jeder versendet und empfängt E-Mails, und viele von euch werden auch eigene Mailserver betreiben, es kann also nicht schaden das Protokoll näher zu kennen. SMTP steht für Simple Mail Transport Protocol, und genau das ist es auch: Simple. Mit einer Hand voll Befehlen kann man E-Mails auf den Weg bringen. SMTP wird von Mailservern gesprochen die E-Mails empfangen oder an andere Mailserver weitergeben, zur E-Mail-Abholung durch einen Client wird IMAP oder POP3 genutzt.

Nehmen wir erstmal den einfachsten Fall: Wir möchten eine E-Mail an meine E-Mail Adresse schicken. Dazu müssen wir zuerst herausfinden welcher Server für diese E-Mail-Adresse zuständig ist. Wir befragen den DNS-Server nach dem sogenannten MX-Eintrag (Mail Exchange) und erhalten den oder die Server, an die wir uns wenden müssen:

dig -t MX phpgangsta.de +short
10 mail.phpgangsta.de.

Hier könnten auch mehrere Ergebnisse erscheinen wenn ich mehrere Mailserver betreiben würde. Wir schauen also als nächstes, wer hinter mail.phpgangsta.de steckt:

dig mail.phpgangsta.de +short
85.214.28.26

In diesem Fall ist es mein Server mit der IP 85.214.28.26, wir müssen unsere E-Mail also dort abgeben. Der SMTP Port ist TCP 25, wohin wir uns ganz einfach mittels Telnet verbinden können:

telnet 85.214.28.26 25
Trying 85.214.28.26...
Connected to 85.214.28.26.
Escape character is '^]'.
220 h1440682.stratoserver.net ESMTP Postfix (Debian/GNU)

Der Server begrüßt uns mit seinem Namen und verrät auch gleichzeitig, dass er nicht nur SMTP spricht, sondern auch Extended SMTP (ESMTP). Mittlerweile sollten alle dieses neue Protokoll unterstützen, das 1995 eingeführt wurde um spezielle Fähigkeiten (Extensions) hinzuzufügen wie beispielsweise Authentifizierung. Vorher gab es die nicht, daran hatte wohl niemand gedacht früher *tzz*. Aber auch TLS ist erst mit ESMTP möglich, heutzutage gibt es auch wahrscheinlich keine Server mehr, die kein ESMTP anbieten.

Da der Server auch TLS unterstützt können wir uns auch verschlüsselt verbinden, indem wir erst eine normale Verbindung (wie oben) aufbauen und dann direkt mit dem STARTTLS Befehl in den verschlüsselten Modus wechseln. Da man das in Telnet schlecht eintippen kann nutzen wir den openssl-Client, der genau dies macht:

openssl s_client -starttls smtp -crlf -connect 85.214.28.26:587
CONNECTED(00000003)
...
...
---
250 HELP

Wir sind also dran uns vorzustellen mit unseren Namen:

HELO michael.client.de
250 h1440682.stratoserver.net

HELO ist der alte Befehl, Hallo zu sagen. Damit schalten wir in den alten SMTP-Modus, was aber eigentlich niemand mehr möchte. Also versuchen wir es erstmal mit EHLO, der ESMTP-Variante. Sollte das fehlschlagen müssen wir auf HELO zurückfallen.

EHLO michael.client.de
250-h1440682.stratoserver.net
250-PIPELINING
250-STARTTLS
250-SIZE 52428800
250-ETRN
250-AUTH PLAIN LOGIN DIGEST-MD5 CRAM-MD5 NTLM RPA
250-AUTH=PLAIN LOGIN DIGEST-MD5 CRAM-MD5 NTLM RPA
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN

Ah, es geht, der Server antwortet mit all seinen Features und Modulen, die er unterstützt sowie der maximalen Größe einer E-Mail, die er annehmen wird.

Die Zahlen am Anfang jeder Antwort des Servers sind die SMTP-Statuscodes.

  • 1XX Mailserver hat die Anforderung akzeptiert, ist aber selbst noch nicht tätig geworden. Eine Bestätigungsmeldung ist erforderlich.
  • 2XX Mailserver hat die Anforderung erfolgreich ohne Fehler ausgeführt.
  • 3XX Mailserver hat die Anforderung verstanden, benötigt aber zur Verarbeitung weitere Informationen.
  • 4XX Mailserver hat einen temporären Fehler festgestellt. Wenn die Anforderung ohne jegliche Änderung wiederholt wird, kann die Verarbeitung möglicherweise abgeschlossen werden.
  • 5XX Mailserver hat einen fatalen Fehler festgestellt. Die Anforderung kann nicht verarbeitet werden.

Der Server kann bei allen Befehlen Fehler zurückliefern, beispielsweise wenn wir einen ungültigen EHLO Befehl abgesendet haben, der Absender oder Empfänger nicht akzeptiert wird, oder der Server festgestellt hat dass die E-Mail Spam ist.

OK, wir haben Hallo gesagt, nun geht es mit der E-Mail los. Wir nennen zuerst den Absender der E-Mail:

MAIL FROM: test@domain.de
250 2.1.0 Ok

Als nächstes folgt der Empfänger der E-Mail:

RCPT TO: XXX@phpgangsta.de
250 2.1.5 Ok

und dann der Inhalt der E-Mail. Dieser besteht aus E-Mail-Headern und dem eigentlichen Inhalt. Ich möchte jetzt hier nicht auf E-Mail-Header eingehen, und auch nicht mehrteilige E-Mails, HTML-Emails, Anhänge usw erklären. Kommt vielleicht später 😉 Wir beginnen mit dem DATA-Befehl, gefolgt vom eigentliche Inhalt. Wir werden angewiesen, das Ende mit einem Punkt in einer eigenen Zeile zu markieren. Hier erstmal eine ganz einfache Text-Email:

DATA
354 End data with <CR><LF>.<CR><LF>
From: bill@microsoft.com
To: empfaenger@domain.de
Subject: Testmail

Testmails sind toll, diese hier ist eine ganz einfache, ohne HTML, ohne Anhänge usw.
.
250 2.0.0 Ok: queued as 370DB1044028

Hier ist schön zu sehen dass wir den From- und To-Header völlig frei festlegen können. Im E-Mail-Programm wird häufig nur dieser leicht fälschbare From-Header angezeigt. Dies ist ein großes Manko des Protokolls finde ich, denn nahezu jeder fällt auf so einen gefälschten Absender rein und erkennt nicht den wirklichen Absender. Das Problem der Absenderfälschung gänzlich zu beheben wird nicht einfach möglich sein, es gibt Ansätze (DKIM, SPF), die aber auch nur partiell funktionieren.

Die E-Mail ist also angenommen worden, wir können uns abmelden mittels QUIT, oder aber die Verbindung aufrecht erhalten und zurücksetzen (RSET), um eine weitere E-Mail abzuliefern.

quit
221 2.0.0 Bye
Connection closed by foreign host.

Ein vollständiger Dialog zur Zustellung einer E-Mail sieht dann so aus:

telnet 85.214.28.26 25
Trying 85.214.28.26...
Connected to 85.214.28.26.
Escape character is '^]'.
220 h1440682.stratoserver.net ESMTP Postfix (Debian/GNU)
EHLO michael.client.de
250-h1440682.stratoserver.net
250-PIPELINING
250-STARTTLS
250-SIZE 52428800
250-ETRN
250-AUTH PLAIN LOGIN DIGEST-MD5 CRAM-MD5 NTLM RPA
250-AUTH=PLAIN LOGIN DIGEST-MD5 CRAM-MD5 NTLM RPA
250-ENHANCEDSTATUSCODES
250-8BITMIME
250 DSN
MAIL FROM: test@domain.de
250 2.1.0 Ok
RCPT TO: XXX@phpgangsta.de
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
From: bill@microsoft.com
To: empfaenger@domain.de
Subject: Testmail

Testmails sind toll, diese hier ist eine ganz einfache, ohne HTML, ohne Anhänge usw.
.
250 2.0.0 Ok: queued as 370DB1044028
quit
221 2.0.0 Bye
Connection closed by foreign host.

OK, E-Mail direkt an einen Mailserver zustellen können wir nun. Doch wie sieht es aus wenn wir die E-Mail dem Mailserver zur weiteren Zustellung übergeben wollen? Das hat mehrere Vorteile: Erstens wird unsere E-Mail, wenn sie vom Provider-Mailserver bzw. dem eigenen Mailserveer im Internet zugestellt wird, seltener als Spam deklariert (manche Mailserver nehmen gar keine E-Mails von dynamischen IP-Adressen an bzw. haben all diese auf Blacklists), und zweitens könnte es ja auch sein dass der Mailserver des Empfängers gerade offline ist, dann müßte man es nach einer Stunde, nach 4 Stunden usw. nochmal probieren (dafür gibt es Queues in allen Mailservern). Das lässt man doch lieber einen Mailserver machen.

Also, wie wollen uns bei unserem Mailserver via SMTP anmelden und ihm eine E-Mail zur weiteren Zustellung übergeben. Dazu müssen wir uns mit dem richtigen Server verbinden und uns authentifizieren. Das sind die sogenannten Postausgangsserver, die meistens smtp.domain.de heißen. Dazu haben wir direkt mehrere Möglichkeiten:

Die meisten (vernünftigen) Mailserver sind nicht nur via Port 25 sondern auch via Port 587 (Submission) bzw. via Port 465 (SSL) erreichbar. Um unsere E-Mail zur weiteren Zustellung abzuliefern verbinden wir uns mit dem eigentlich dafür vorgesehenen Port 587:

telnet 85.214.28.26 587
Trying 85.214.28.26...
Connected to 85.214.28.26.
Escape character is '^]'.
220 h1440682.stratoserver.net ESMTP Postfix (Debian/GNU)

Um die E-Mail nicht im Klartext übertragen zu müssen können wir wieder TLS nutzen (siehe oben) oder auch SSL:

openssl s_client -crlf -connect 85.214.28.26:465
CONNECTED(00000003)
...
...
---
220 HELP

Und nun sagen wir wieder brav EHLO:

EHLO michael.client.de
 250-h1440682.stratoserver.net
 250-PIPELINING
 250-STARTTLS
 250-SIZE 52428800
 250-ETRN
 250-AUTH PLAIN LOGIN DIGEST-MD5 CRAM-MD5 NTLM RPA
 250-AUTH=PLAIN LOGIN DIGEST-MD5 CRAM-MD5 NTLM RPA
 250-ENHANCEDSTATUSCODES
 250-8BITMIME
 250 DSN

Damit wir nun einen Empfänger definieren können der nicht auf diesem Mailserver beheimatet ist müssen wir uns erstmal authentifizieren. Dazu bietet der Mailserver die oben gelisteten Methoden. Da wir via TLS oder SSL verschlüsselt kommunizieren können wir eine unsichere Methode nehmen bei der wir den Usernamen und das Passwort nur via base64 kodieren. Sollten wir unverschlüsselt mit dem Server verbunden sein bietet es sich an DIGEST-MD5, CRAM-MD5 oder eine andere sichere Authentifizierungsmethode zu wählen.

Wir nehmen die einfachste: AUTH LOGIN. Dann müssen wir nacheinander den Benutzernamen und das Passwort übergeben, jeweils base64 kodiert:

AUTH LOGIN
334 VXNlcm5hbWU6
aGFoYUBwaHBnYW5nc3RhLmRl
334 UGFzc3dvcmQ6
bWVpbnBhc3N3b3J0
235 2.7.0 Authentication successful

Zwei kurze Zeilen helfen mir dabei immer:

php -r 'echo base64_encode("email@domain.de")."\n";'
php -r 'echo base64_encode("GeheimPasswortHier")."\n";'

Alternativ dazu gibt es noch AUTH PLAIN, bei dem beides in einem Befehl übergeben wird, getrennt durch ein Null-Byte:

auth plain AGVtYWlsQGRvbWFpbi5kZQBHZWhlaW1QYXNzd29ydEhpZXI=
235 2.7.0 Authentication successful

 

php -r 'echo base64_encode("\0email@domain.de\0GeheimPasswortHier")."\n";'

Das ältere Verfahren SMTP-after-POP wird immer seltener unterstützt, da war es nötig sich erstmal per POP3 zu authentifizieren, und dann hatte man ein Zeitfenster von beispielsweise 15 Minuten Zeit, E-Mails via SMTP zu verschicken. Das war ein Workaround aus der Zeit wo es noch kein ESMTP gab.

Gut, wir sind authentifiziert, und können nun genauso wie oben die E-Mail verschicken:

MAIL FROM: test@domain.de
250 2.1.0 Ok
RCPT TO: irgendwen@dadraussen.de
250 2.1.5 Ok
DATA
354 End data with <CR><LF>.<CR><LF>
From: test@domain.de
To: irgendwen@dadraussen.de
Subject: Testmail

Testmails sind toll, diese hier ist eine ganz einfache, ohne HTML, ohne Anhänge usw.
.
250 2.0.0 Ok: queued as 370DB1044028
quit
221 2.0.0 Bye
Connection closed by foreign host.

Ich glaube mehr gibt es nicht an Grundlagen zu SMTP zu erzählen, alles weitere ist technischer Schnickschnack den nur Mailserver untereinander brauchen 🙂

Written by Michael Kliewe

August 8th, 2011 at 9:38 am

3 Responses to 'Das SMTP Protokoll im Detail betrachtet'

Subscribe to comments with RSS or TrackBack to 'Das SMTP Protokoll im Detail betrachtet'.

  1. […] 17.11.2011 • Probleme mit Umlauten und Sonderzeichen in PHP • Closures in PHP • Das SMTP Protokoll im Detail betrachtet • Choose your web fonts wisely • The Definitive Guide To Forms based Website […]

  2. wie kann ich die über SMTP versendete Mail in den Ordner Gesendete speichern?

    Peter

    3 Jan 12 at 14:51

  3. @Peter: Dazu muss man die E-Mail (also das was nach dem DATA bei SMTP geschickt wird) noch zusätzlich per IMAP in den Ordner legen. Das geht dann mit dem APPEND Befehl, siehe
    https://www.phpgangsta.de/das-imap-protokoll-im-detail-betrachtet

    Michael Kliewe

    3 Jan 12 at 15:18

Leave a Reply

You can add images to your comment by clicking here.