Odosielanie cez SMTP v Pythone: STARTTLS na 587 alebo TLS na 465?
STARTTLS na 587 a implicitný TLS na 465 oba prenášajú odoslanie z Pythonu, ale vášmu relé vyhovuje iba jeden. Porovnajte nadviazanie spojenia a spôsoby zlyhania.
Oba režimy pripojenia prenášajú tú istú správu na ten istý účet relé a rozdiel je v tom, kde sa začína TLS, nie v tom, čo sa odosiela. Jeden sa otvára v otvorenom texte a upgraduje sa v rámci spojenia; druhý je šifrovaný od prvého bajtu. Tento jediný rozdiel rozhoduje o tom, čo prijme striktná politika relé, čo môže pozorovať medziľahlé zariadenie a ktorú chybovú správu strávite popoludním čítaním. Takto medzi nimi volím, keď aplikácia v privátnej sieti odovzdáva správu internej službe odosielania.
Kde sa oba režimy skutočne rozchádzajú
Úloha je v oboch prípadoch rovnaká: autentizovať sa ako schválený účet, uviesť povoleného odosielateľa, vymenovať príjemcov a čakať, kým relé prevezme zodpovednosť. Mení sa poradie operácií. Port 587 začína ako relácia SMTP v otvorenom texte, inzeruje STARTTLS v odpovedi EHLO a upgraduje sa iba vtedy, ak o to klient požiada. Port 465 očakáva TLS handshake skôr, než zaznie akýkoľvek príkaz SMTP.
Odosielanie a relé sú rôzne úlohy, a preto čísla portov nie sú zameniteľné zvyky. RFC 6409 opisuje odosielanie správ ako samostatnú službu a RFC 8314 pokrýva variant implicitného TLS, ktorý väčšina moderných relé vystavuje na 465. Ak vaše relé dokumentuje jeden z tých dvoch, táto dokumentácia má prednosť pred akoukoľvek preferenciou z predchádzajúceho zamestnania.
Čo vám STARTTLS na 587 dáva
Upgrade prebieha vnútri konverzácie SMTP, takže relé ho môže inzerovať a klient môže odmietnuť pokračovať bez neho. V Pythone starttls() vyhodí výnimku, keď server toto rozšírenie neponúka, čo pokus o downgrade zmení na viditeľné zlyhanie namiesto tichého prihlásenia v otvorenom texte. Toto správanie je hlavným argumentom pre tento režim a stojí za to sa naň spoliehať zámerne, nie náhodou.
Okno expozície je malé, ale skutočné: počiatočná výmena EHLO je čitateľná a nič pred upgradom nenesie prihlasovacie údaje. Na privátnej ceste medzi podsietou aplikácie a interným relé je toto okno zvyčajne prijateľné. Na ceste, ktorú nekontrolujete, je to dôvod, prečo niektoré tímy trvajú na druhom režime.
Zariadenie na odpočúvanie, ktoré odstráni schopnosť STARTTLS, vytvorí neúspešné odoslanie, nie návrat. To je výsledok, ktorý chcete, a zároveň najrýchlejší spôsob, ako zistiť, že niečo na ceste prepisuje SMTP.
Čo mení implicitný TLS na 465
Fáza otvoreného textu neexistuje vôbec. Klient vykoná TLS handshake hneď po otvorení soketu, overí certifikát proti svojmu úložisku dôvery a až potom vymení EHLO, AUTH a správu. Nič nemôže downgradovať reláciu, pretože neexistuje stav otvoreného textu, z ktorého by sa dalo vyjednávať, a to odstraňuje celú triedu zlyhaní pri odpočúvaní.
Kompromisom je diagnostikovateľnosť. Relé, ktoré na 465 odpovedá otvoreným SMTP, alebo load balancer, ktorý ukončí TLS s nesprávnym názvom certifikátu, zlyhá pri handshaku a dá vám chybu SSL namiesto chyby pošty. Keď sa to stane, problém je pod aplikáciou a žiadne zostavovanie správy ho neopraví.
Jedna funkcia odosielania, dva transporty
Kód sa medzi režimami sotva líši, a preto by sa voľba mala robiť podľa politiky relé, nie podľa pohodlia knižnice. Najprv exportujte nastavenia a potom spustite jediný skript, ktorý vyberie transport podľa nakonfigurovaného portu.
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() overuje reťazec a názov hostiteľa, takže privátnu CA treba nainštalovať do úložiska dôvery, nie obísť neovereným kontextom. send_message() vracia slovník príjemcov, ktorých relé odmietlo; prázdny slovník znamená, že prijalo všetkých. Pätnásťsekundový časový limit je východiskový bod, nie hodnota vyladená pre vašu sieť.
Držte heslo mimo image a mimo logov. Tajomstvo pripojené za behu a načítané do prostredia je v poriadku; vypisovať prostredie do riadku logu nie je, rovnako ako ladiaca stopa výmeny AUTH.
Vyberajte podľa politiky relé a cesty, nie podľa zvyku
Rozhodujú tri podmienky. Po prvé, čo relé skutočne inzeruje: služba, ktorá počúva len na 587 so STARTTLS, nie je dostupná na 465, nech už implicitnému TLS fandíte akokoľvek. Po druhé, či je cesta medzi aplikáciou a relé dosť dôveryhodná na to, aby prijala čitateľnú výmenu EHLO. Po tretie, či niečo uprostred prepisuje alebo proxuje SMTP, pretože to rozhoduje o tom, či je riziko downgrade teoretické, alebo skutočné.
Keď relé ponúka oboje, beriem 465 pre službu, ktorá beží bez dozoru, a 587 pre tú, pri ktorej človek sleduje prvé nasadenie, jednoducho preto, že chybové správy ukazujú na rôzne vrstvy. Tá preferencia je moja a nie je to politika.
Čokoľvek si vyberiete, zaznamenajte port, dôveryhodný materiál a doménu odosielateľa spolu s nasadením. Potom pošlite jednu autorizovanú testovaciu správu a potvrďte, že ju relé prijalo, pričom výsledok odoslania a akýkoľvek neskorší report o doručení uchovajte ako samostatné záznamy. Úspešné odovzdanie relé je koniec zodpovednosti aplikácie, nie dôkaz, že správa dorazila do schránky.