Beide Verbindungsmodi transportieren dieselbe Nachricht zum selben Relay-Konto, und der Unterschied liegt darin, wo TLS beginnt, nicht darin, was gesendet wird. Der eine öffnet im Klartext und wird innerhalb der Verbindung hochgestuft; der andere ist vom ersten Byte an verschlüsselt. Dieser eine Unterschied entscheidet, was eine strenge Relay-Richtlinie akzeptiert, was eine Zwischenstation beobachten kann und welche Fehlermeldung Sie einen Nachmittag lang lesen werden. So wähle ich zwischen ihnen, wenn eine Anwendung in einem privaten Netz eine Nachricht an einen internen Submissionsdienst übergibt.

Wo die beiden Modi tatsächlich auseinandergehen

Die Aufgabe ist in beiden Fällen identisch: sich als freigegebenes Konto authentifizieren, einen erlaubten Absender nennen, Empfänger auflisten und darauf warten, dass das Relay die Verantwortung übernimmt. Was sich ändert, ist die Reihenfolge der Operationen. Port 587 beginnt als Klartext-SMTP-Sitzung, kündigt STARTTLS in der EHLO-Antwort an und wird nur hochgestuft, wenn der Client danach fragt. Port 465 erwartet einen TLS-Handshake, bevor irgendein SMTP-Befehl gesprochen wird.

Submission und Relay sind verschiedene Aufgaben, weshalb die Portnummern keine austauschbaren Gewohnheiten sind. RFC 6409 beschreibt die Nachrichtensubmission als eigenen Dienst, und RFC 8314 deckt die implizite TLS-Variante ab, die die meisten modernen Relays auf 465 anbieten. Wenn Ihr Relay eines der beiden dokumentiert, geht diese Dokumentation jeder Vorliebe vor, die Sie von einem früheren Arbeitgeber mitbringen.

Was STARTTLS auf 587 Ihnen gibt

Die Hochstufung geschieht innerhalb der SMTP-Konversation, sodass das Relay sie ankündigen kann und der Client sich weigern kann, ohne sie fortzufahren. In Python löst starttls() eine Ausnahme aus, wenn der Server die Erweiterung nicht anbietet, was einen Downgrade-Versuch in einen sichtbaren Fehler verwandelt statt in einen stillen Klartext-Login. Dieses Verhalten ist das Hauptargument für den Modus, und es lohnt sich, sich bewusst darauf zu verlassen statt zufällig.

Das Expositionsfenster ist klein, aber real: der anfängliche EHLO-Austausch ist lesbar, und nichts vor der Hochstufung trägt Anmeldedaten. Auf einem privaten Pfad zwischen einem Anwendungssubnetz und einem internen Relay ist dieses Fenster meist akzeptabel. Auf einem Pfad, den Sie nicht kontrollieren, ist es der Grund, warum manche Teams auf dem anderen Modus bestehen.

Ein Abfanggerät, das die STARTTLS-Fähigkeit entfernt, erzeugt eine fehlgeschlagene Submission, keinen Rückfall. Das ist das gewünschte Ergebnis, und es ist zugleich der schnellste Weg herauszufinden, dass etwas auf dem Pfad SMTP umschreibt.

Was implizites TLS auf 465 ändert

Es gibt überhaupt keine Klartextphase. Der Client führt einen TLS-Handshake aus, sobald der Socket geöffnet wird, prüft das Zertifikat gegen seinen Trust Store und tauscht erst danach EHLO, AUTH und die Nachricht aus. Nichts kann die Sitzung herabstufen, weil es keinen Klartextzustand gibt, über den verhandelt werden könnte, und das entfernt eine ganze Klasse von Abfangfehlern.

Der Preis ist die Diagnostizierbarkeit. Ein Relay, das auf 465 mit Klartext-SMTP antwortet, oder ein Load Balancer, der TLS mit dem falschen Zertifikatsnamen beendet, scheitert am Handshake und liefert Ihnen einen SSL-Fehler statt eines Mail-Fehlers. Wenn das passiert, liegt das Problem unterhalb der Anwendung, und keine noch so sorgfältige Nachrichtenkonstruktion behebt es.

Eine Submission-Funktion, zwei Transporte

Der Code unterscheidet sich kaum zwischen den Modi, weshalb die Wahl aus der Relay-Richtlinie und nicht aus der Bequemlichkeit der Bibliothek getroffen werden sollte. Exportieren Sie zuerst die Einstellungen und führen Sie dann ein einzelnes Skript aus, das den Transport anhand des konfigurierten Ports auswählt.

export SMTP_HOST=smtp.relay.internal
export SMTP_PORT=587
export SMTP_USER=svc-notify
export [email protected]
export [email protected]
export SMTP_PASSWORD="$(cat /run/secrets/smtp_password)"
import os, smtplib, ssl
from email.message import EmailMessage

ctx = ssl.create_default_context()
msg = EmailMessage()
msg["From"] = os.environ["MAIL_FROM"]
msg["To"] = os.environ["MAIL_TEST_TO"]
msg["Subject"] = "relay submission test"
msg.set_content("One authorized test message from the app.")

host = os.environ["SMTP_HOST"]
port = int(os.environ.get("SMTP_PORT", "587"))

if port == 465:
    client = smtplib.SMTP_SSL(host, port, context=ctx, timeout=15)
else:
    client = smtplib.SMTP(host, port, timeout=15)
    client.ehlo()
    client.starttls(context=ctx)
    client.ehlo()

with client:
    client.login(os.environ["SMTP_USER"], os.environ["SMTP_PASSWORD"])
    refused = client.send_message(msg)

print("refused:", refused)

create_default_context() prüft die Kette und den Hostnamen, daher muss eine private CA in den Trust Store installiert werden, statt sie mit einem ungeprüften Kontext zu umgehen. send_message() gibt ein Wörterbuch der Empfänger zurück, die das Relay abgelehnt hat; ein leeres Wörterbuch bedeutet, dass es alle akzeptiert hat. Das Timeout von fünfzehn Sekunden ist ein Ausgangspunkt, kein auf Ihr Netz abgestimmter Wert.

Halten Sie das Passwort aus dem Image und aus den Logs. Ein Geheimnis, das zur Laufzeit eingebunden und in die Umgebung gelesen wird, ist in Ordnung; die Umgebung in eine Logzeile zu spiegeln ist es nicht, und ein Debug-Trace des AUTH-Austauschs ebenso wenig.

Wählen Sie nach Relay-Richtlinie und Pfad, nicht nach Gewohnheit

Drei Bedingungen entscheiden es. Erstens, was das Relay tatsächlich ankündigt: ein Dienst, der nur auf 587 mit STARTTLS lauscht, ist auf 465 nicht erreichbar, egal wie sehr Sie implizites TLS bevorzugen. Zweitens, ob der Pfad zwischen Anwendung und Relay vertrauenswürdig genug ist, um einen lesbaren EHLO-Austausch zu akzeptieren. Drittens, ob etwas dazwischen SMTP umschreibt oder proxyt, denn das entscheidet, ob das Downgrade-Risiko theoretisch oder real ist.

Wenn das Relay beides anbietet, nehme ich 465 für einen Dienst, der unbeaufsichtigt läuft, und 587 für einen, bei dem ein Mensch den ersten Einsatz beobachtet, allein weil die Fehlermeldungen auf verschiedene Schichten zeigen. Diese Vorliebe ist meine, und sie ist keine Richtlinie.

Was auch immer Sie wählen, protokollieren Sie den Port, das Vertrauensmaterial und die Absenderdomain zusammen mit dem Deployment. Senden Sie dann eine autorisierte Testnachricht und bestätigen Sie, dass das Relay sie angenommen hat, wobei Sie das Submission-Ergebnis und jeden späteren Zustellbericht als getrennte Aufzeichnungen führen. Eine erfolgreiche Übergabe an das Relay ist das Ende der Verantwortung der Anwendung, nicht der Beweis, dass die Nachricht ein Postfach erreicht hat.

Ähnliche Artikel