Leitfaden · Python 3 SMTP
Wie sollte ein Produktteam Python 3 SMTP sicher implementieren?
Implementieren Sie Python 3 SMTP hinter einem autorisierten Server-Worker, nicht in Browser- oder nutzerkontrolliertem Code. Bauen Sie Nachrichten mit EmailMessage, halten Sie Envelope-Empfänger getrennt von sichtbaren Headern, erstellen Sie einen verifizierten SSL-Kontext, setzen Sie endliche Verbindungs-Timeouts und verwenden Sie entweder SMTP_SSL für TLS ab Verbindungsbeginn oder SMTP.starttls() gefolgt von EHLO für ein explizites Upgrade. Laden Sie Zugangsdaten aus einem Secret Manager, rufen Sie send_message() auf, prüfen Sie die Ergebnisse zu abgelehnten Empfängern und persistieren Sie das exakte Ergebnis jedes Versuchs. Wiederholen Sie nur vorübergehende Fehler mit begrenztem Backoff und werten Sie die SMTP-Annahme niemals als Beleg für die Platzierung im Posteingang.
Eine autorisierte E-Mail-Operation definieren
Gehen Sie von einem freigegebenen Produkt-Event aus, etwa einer Kontoverifizierung, einem Beleg, einer angeforderten Benachrichtigung oder einer Sicherheitsmeldung. Speichern Sie einen dauerhaften Outbound-Job, bevor Sie eine SMTP-Verbindung öffnen. Dieser Job sollte einen stabilen Schlüssel für das Geschäftsereignis, Mandant, Nachrichtenklasse, Template-Revision, freigegebenen Envelope-Absender und Empfänger, die sichtbare From-Identität, die Einwilligungs- oder Erforderlichkeitsgrundlage und das aktuelle Ergebnis der Sperrlistenprüfung enthalten. Eingaben aus Browser, Mobile-App, Template und von Nutzern dürfen weder SMTP-Hosts noch Zugangsdaten, Envelope-Absender, beliebige Header oder uneingeschränkte Empfänger festlegen. Autorisieren Sie Aufrufer und Mandant, validieren Sie Adressen, begrenzen Sie die Anzahl der Empfänger und Anhänge und verhindern Sie Newline-Injection. Beanspruchen Sie einen Job nur einmal und führen Sie eine Append-only-Historie der Versuche. Pythons smtplib ist ein Protokoll-Client; er bietet weder geschäftliche Idempotenz noch Mandantentrennung, Einwilligung, Sperrliste oder eine dauerhafte Warteschlange. Diese Kontrollen gehören in die Anwendung darum herum.
Strukturierte Nachrichten mit EmailMessage bauen
Verwenden Sie email.message.EmailMessage, statt Header- und MIME-Strings zu verketten. Setzen Sie From, To, Subject und einen stabilen Korrelations-Header der Anwendung aus validierten Werten, rufen Sie dann set_content für Plain-Text auf, add_alternative für einen HTML-Teil, falls nötig, und add_attachment nur für ausdrücklich unterstützte Dateitypen und Größen. Erzeugen Sie Text und HTML aus derselben freigegebenen Template-Revision. Maskieren Sie nicht vertrauenswürdige Werte passend zum Ausgabekontext und rendern Sie kein rohes Nutzer-HTML. Legen Sie keine Geheimnisse, Zugriffstokens, unnötigen personenbezogenen Daten oder internen Datenbankschlüssel in Headern, Betreffzeilen, Tracking-Feldern oder Anhangsnamen ab. Das Paket email serialisiert gemäß seiner Policy und kann beim Flattening MIME-Grenzen erzeugen; signieren oder hashen Sie daher die endgültige serialisierte Darstellung, wenn spätere Integritätskontrollen von den exakten Bytes abhängen. Halten Sie den SMTP-Envelope getrennt: Die sichtbaren Header To und Cc informieren die Leser, während die Empfängerliste des Transports die RCPT-TO-Befehle steuert.
Implizites TLS oder STARTTLS bewusst wählen
Verwenden Sie SMTP_SSL, wenn der Server TLS von Beginn der Verbindung an verlangt. Verwenden Sie SMTP für eine unverschlüsselte Verbindung nur dann, wenn der dokumentierte Ablauf des Servers ein sofortiges STARTTLS-Upgrade verlangt. Die Dokumentation zu smtplib in Python besagt, dass starttls die nachfolgenden SMTP-Befehle in TLS einbettet und der Client danach erneut ehlo aufrufen soll. Authentifizieren Sie sich niemals vor dem erforderlichen TLS-Upgrade. Erstellen Sie den Kontext mit ssl.create_default_context, damit Zertifikatsprüfung und Hostnamenprüfung sichere Client-Standardwerte nutzen, und übergeben Sie den erwarteten Server-Hostnamen über die normale Verbindung der Bibliothek. Behandeln Sie fehlende STARTTLS-Unterstützung, Zertifikatsfehler, Hostnamen-Abweichung oder gescheiterte TLS-Aushandlung als harten Stopp, wenn Verschlüsselung erforderlich ist. Deaktivieren Sie die Verifizierung nicht und ersetzen Sie sie nicht durch einen ungeprüften Kontext, nur damit der Produktivbetrieb läuft. TLS pro Hop schützt die SMTP-Verbindung, nicht gespeicherte Nachrichteninhalte, die Verarbeitung beim Provider, die Speicherung beim Empfänger oder das endgültige Postfach.
Zugangsdaten serverseitig und eingeschränkt halten
Laden Sie SMTP-Benutzernamen, Passwort oder Token zur Laufzeit aus einem verwalteten Secret-Dienst. Legen Sie sie niemals in Versionsverwaltung, Docker-Layern, in Git eingecheckter Konfiguration, URLs, Kommandozeilenargumenten, Debug-Ausgaben, Analytics, Exception-Reports, Test-Snapshots, Notebooks, Tickets oder Prompts ab. Bevorzugen Sie ein Credential, das auf eine Umgebung, Absenderdomain oder zulässige Workload beschränkt ist, gegenüber einem kontoweiten administrativen Geheimnis. Trennen Sie Produktion von Entwicklung und CI. Machen Sie die Rotation zur Routine: Erstellen Sie ein Ersatz-Credential, aktualisieren Sie den Worker, führen Sie einen kontrollierten Zustelltest durch, bestätigen Sie Authentifizierung und Ergebnisnachweise und widerrufen Sie dann das alte Credential. Begrenzen Sie den Zugriff auf Geheimnisse auf den Versandprozess und protokollieren Sie administrative Lesezugriffe. Die Methode login in Python probiert die vom Server angebotenen Authentifizierungsmechanismen; die Anwendung muss dennoch entscheiden, ob Server, Verbindungssicherheit, Konto und Mechanismus akzeptabel sind. Wiederholte Authentifizierungsfehler sollten die Kohorte pausieren und eine Untersuchung auslösen, statt Passwörter schnell erneut zu probieren.
Explizite Timeouts und begrenzte Verbindungslebensdauer verwenden
Übergeben Sie SMTP oder SMTP_SSL ein endliches Timeout, damit Verbindungs- und blockierende Operationen einen Worker nicht unbegrenzt belegen können. Wenden Sie eine äußere Job-Deadline und eine Abbruchrichtlinie an, denn ein Socket-Timeout ist keine vollständige Kontrolle über das Alter in der Warteschlange. Halten Sie kein gemeinsames SMTP-Objekt über nebenläufige Tasks hinweg, sofern der Zugriff nicht serialisiert und sein Zustand nachweislich sicher ist. Ein einfacher Entwurf öffnet eine Verbindung für einen begrenzten Batch, begrüßt den Server, baut bei Bedarf TLS auf, authentifiziert sich, übermittelt wenige Nachrichten, ruft quit auf und verwirft die Verbindung nach Fehlern oder Altersgrenzen. Wiederverwendung kann den Overhead senken, erhöht aber die Mehrdeutigkeit nach Verbindungsabbrüchen des Servers, Timeouts oder Teilzuständen. Begrenzen Sie die Nachrichten pro Verbindung und verbinden Sie sich bewusst neu. Überwachen Sie Verbindungslatenz, TLS-Aushandlung, Authentifizierung, Latenz der Befehle, Verbindungsabbrüche des Servers und Job-Alter, ohne Zugangsdaten oder Nachrichteninhalte zu loggen. Der SMTP-Server kann Limits setzen, die sich unabhängig von Python ändern.
Eine Nachricht senden und Ergebnisse pro Empfänger speichern
SMTP.sendmail nutzt from_addr und to_addrs für den Transport-Envelope und schreibt die Nachrichten-Header nicht um. SMTP.send_message serialisiert eine EmailMessage und leitet Standardwerte ab, sofern keine expliziten Envelope-Werte angegeben werden. Übergeben Sie im Produktivcode den freigegebenen Envelope-Absender und die Empfängerliste ausdrücklich, damit Bcc-Behandlung und Mandantenautorisierung eindeutig bleiben. Laut Python-Dokumentation kehrt sendmail normal zurück, wenn mindestens ein Empfänger akzeptiert wurde, und liefert ein Dictionary mit jedem abgelehnten Empfänger. Ein ausbleibender Ausnahmepfad bedeutet daher nicht, dass alle Empfänger erfolgreich waren. Speichern Sie akzeptierte und abgelehnte Empfänger getrennt, einschließlich Statuscode und bereinigter Diagnose. Wiederholen Sie akzeptierte Empfänger nicht, wenn nur einige Empfänger abgelehnt wurden. Behandeln Sie jeden Empfänger als unabhängig autorisiertes Ergebnis und behalten Sie dabei den gemeinsamen Nachrichtenversuch bei. Eine spätere Ausnahme in der DATA-Phase unterscheidet sich von einer RCPT-Ablehnung und braucht eine eigene Klassifizierung.
Ausnahmen nach Phase und Dauerhaftigkeit klassifizieren
Behandeln Sie smtplib-Ausnahmen explizit und bewahren Sie ihre SMTP-Codes und bereinigten Servermeldungen auf. SMTPConnectError und Timeout können vorübergehend sein, können aber auch auf falschen Host, falschen Port, Firewall oder Ausfall hinweisen. SMTPNotSupportedError nach STARTTLS oder SMTPUTF8 sollte eine Konfiguration stoppen, die die Funktion verlangt. SMTPAuthenticationError erfordert eine Untersuchung von Credential, Konto, Mechanismus und TLS, keinen blinden Wiederholungsversuch. SMTPSenderRefused und SMTPRecipientsRefused erfordern Entscheidungen pro Identität oder Empfänger. SMTPDataError beschreibt eine unerwartete DATA-Antwort und kann je nach erweitertem Status auf Inhalt, Richtlinie, Kontingent oder vorübergehendes Verhalten des Empfängers hindeuten. Klassifizieren Sie 4xx-Antworten als Kandidaten für begrenzte Wiederholungsversuche und 5xx als permanent für diesen Versuch, und beachten Sie dabei die Dokumentation des jeweiligen Providers. Nutzen Sie exponentiellen Backoff, Jitter, Obergrenzen für Versuche und Alter in der Warteschlange sowie einen Dead-Letter-Status. Wiederholen Sie niemals nach Hinweisen auf Sperrliste, Beschwerde, Abmeldung, widerrufene Autorisierung oder ungültigen Empfänger.
Mehrdeutige Übermittlungsergebnisse abgleichen
Ein Netzwerk-Timeout oder Verbindungsabbruch, nachdem der Client die Nachrichtendaten übertragen, die endgültige Serverantwort aber nicht mehr beobachtet hat, ist mehrdeutig. Der Server könnte die Verantwortung übernommen haben, obwohl Python eine Ausnahme ausgelöst hat. Legen Sie nicht sofort einen neuen logischen Versand an. Markieren Sie den Versuch als unbekannt, behalten Sie seine stabilen Event- und Trace-Kennungen und fragen Sie, sofern verfügbar, Provider-Logs oder spätere Zustell-Events ab. Bietet der SMTP-Dienst weder Idempotenz noch durchsuchbare Korrelation, definieren Sie eine Produktentscheidung anhand von Nachrichtenklasse, Alter, Schaden durch Duplikate und Nutzererlebnis. Sicherheitswarnungen und Nachrichten zum Zurücksetzen von Passwörtern haben andere Duplikatrisiken als Belege oder Finanzbenachrichtigungen. Bewahren Sie den ursprünglichen Versuch und jede Verknüpfung zu Wiederholungen im Ledger auf. Versprechen Sie niemals Exactly-once-Zustellung, denn SMTP bietet sie nicht Ende zu Ende. Testen Sie diesen Zweig mit einem kontrollierten Server-Fixture, das die Verbindung in jeder Protokollphase trennt, auch vor und nach der Annahme von DATA.
SMTP-Annahme von Zustellung und Interaktion trennen
Ein erfolgreicher Aufruf von send_message bedeutet nach der dokumentierten Semantik von Python, dass in der beobachteten SMTP-Phase mindestens ein Empfänger akzeptiert wurde. Er belegt nicht, dass jeder Empfänger akzeptiert wurde, dass der Zielserver die Nachricht später behalten hat, dass sie in einem Posteingangsordner landete oder dass eine Person sie gelesen hat. Modellieren Sie Übermittlung an den Provider, Annahme durch den Empfängerserver, vorübergehenden oder permanenten Fehler, späteren Bounce, Beschwerde, Abmeldung, Postfachplatzierung und Interaktion als getrennte Nachweise. Übernehmen Sie authentifizierte Provider-Events, sofern verfügbar, deduplizieren Sie sie und speichern Sie den Zeitpunkt des Ereignisses getrennt vom Zeitpunkt der Verarbeitung. Setzen Sie permanente Bounces, Beschwerden und Abmeldungen unmittelbar vor späteren Sendungen durch. Öffnungen und Klicks sind kein Transportnachweis und können durch Datenschutztechnik verfälscht werden. Führen Sie datenschutzminimierte aggregierte Kennzahlen nach mandantensicherer Kohorte, Template-Revision, Absenderdomain, Statusklasse und Zeit. Lösen Sie Alarme aus bei Anstiegen von Ablehnungen, unbekannten Ergebnissen, Alter in der Warteschlange, TLS-Fehlern, Authentifizierungsfehlern und ungewöhnlichem Empfänger-Fan-out.
Lokal testen, ohne echte Kunden-E-Mails zu senden
Testen Sie Nachrichtenzusammenstellung, Ablehnung von Header-Injection, Empfängerautorisierung, Bcc-Entfernung, Plain- und HTML-Alternativen, Unicode-Behandlung, Anhangsgrenzen und Sperrlistenprüfungen mit Unit-Tests. Verwenden Sie einen kontrollierten lokalen SMTP-Testserver oder ein Protokoll-Fixture, um Begrüßungsfehler, fehlendes STARTTLS, Zertifikatsfehler, Authentifizierungsfehler, teilweise RCPT-Annahme, DATA-Antworten 4xx und 5xx, Verbindungsabbrüche und verzögerte Antworten zu simulieren. Verwenden Sie keine veralteten, nicht authentifizierten Debugging-Dienste für produktionsähnliche Geheimnisse oder Kundeninhalte. Integrationstests sollten dedizierte Konten und kontrollierte Empfänger mit expliziten Kontingenten und Bereinigung verwenden. Prüfen Sie die rohe empfangene Nachricht, Authentifizierungsergebnisse, sichtbare Header, Antwortverhalten und Event-Korrelation. Führen Sie Secret-Scans für Fixtures und Logs aus.
So passt SendHQ
SendHQ dokumentiert eine auf den Workspace beschränkte E-Mail-API für Versand über verifizierte Domains, Zustell-Events und Sperrlisten. Dieser Leitfaden behandelt den SMTP-Client der Python-Standardbibliothek; verwenden Sie die SendHQ-Dokumentation für aktuelle Integrationsmethoden und den API-Vertrag.
Häufig gestellte Fragen
Sollten Python-SMTP-Zugangsdaten in Client-Code stehen?
Nein. Bewahren Sie sie in einem serverseitigen Secret Manager mit engem Umgebungs- und Workload-Bezug, protokolliertem Zugriff, regelmäßiger Rotation und ohne Logging auf.
Wann sollte Python SMTP_SSL verwenden?
Verwenden Sie SMTP_SSL, wenn TLS von Verbindungsbeginn an erforderlich ist. Verwenden Sie SMTP mit starttls nur für einen dokumentierten Ablauf mit explizitem Upgrade, der im Fehlerfall abbricht (fail closed).
Sollte nach starttls erneut EHLO aufgerufen werden?
Ja. Die Dokumentation zu smtplib in Python sieht vor, nach starttls erneut ehlo aufzurufen, damit die Fähigkeiten innerhalb der geschützten Verbindung neu ermittelt werden.
Bedeutet send_message, dass jeder Empfänger akzeptiert wurde?
Nein. Python kann normal zurückkehren, wenn mindestens ein Empfänger akzeptiert wurde, und liefert abgelehnte Empfänger gesondert zurück. Speichern und behandeln Sie die Ergebnisse pro Empfänger unabhängig voneinander.
Was sollte nach einem SMTPAuthenticationError geschehen?
Pausieren Sie die betroffene Konfiguration und prüfen Sie TLS, Server, Konto, Geheimnis und angebotene Mechanismen. Blindes Wiederholen von Zugangsdaten kann Sperrungen oder Kompromittierungssignale verstärken.
Sollte jeder SMTPDataError wiederholt werden?
Nein. Bewahren Sie exakten Status und Diagnose auf und unterscheiden Sie dann temporäre 4xx-Bedingungen von permanenten 5xx-Fehlern bei Richtlinie, Inhalt, Kontingent oder Konfiguration.
Bestimmt die SMTP-Annahme die Platzierung im Posteingang?
Nein. Sie ist ein eng abgegrenzter Transportnachweis. Weitere Relays, Filterung beim Empfänger, Bounces, Postfachregeln, Ordnerplatzierung und menschliche Interaktion bleiben getrennte Ergebnisse.
Behandelt dieser Leitfaden eine SendHQ-spezifische Integration?
Nein. Es behandelt den SMTP-Client der Python-Standardbibliothek. Konsultieren Sie die SendHQ-Dokumentation für aktuelle Integrationsmethoden und den API-Vertrag.
Quellen
- Dokumentation zu Python 3 smtplib — Python Software Foundation
- Dokumentation zu Python 3 EmailMessage — Python Software Foundation
- Dokumentation zu Python 3 ssl — Python Software Foundation
- RFC 5321: Simple Mail Transfer Protocol — RFC-Editor
- RFC 3207: SMTP Service Extension for Secure SMTP over TLS — RFC-Editor
- RFC 4954: SMTP Service Extension for Authentication — RFC Editor