Pošiljanje prek SMTP v Pythonu: STARTTLS na 587 ali TLS na 465?
STARTTLS na 587 in implicitni TLS na 465 oba prenašata pošiljanje iz Pythona, a vašemu relayu ustreza le eden. Primerjajte rokovanje in načine napak.
Oba načina povezave prenašata isto sporočilo na isti relay račun, razlika pa je v tem, kje se začne TLS, ne v tem, kaj se pošlje. Eden se odpre v čistem besedilu in se nadgradi znotraj povezave; drugi je šifriran od prvega bajta. Ta ena razlika odloči, kaj bo sprejela stroga relay politika, kaj lahko opazi vmesna naprava in katero sporočilo o napaki boste preživeli popoldne ob branju. Tako izbiram med njima, ko aplikacija v zasebnem omrežju preda sporočilo notranji storitvi za pošiljanje.
Kje se načina res razlikujeta
Naloga je v obeh primerih enaka: overiti se kot odobren račun, navesti dovoljenega pošiljatelja, našteti prejemnike in počakati, da relay prevzame odgovornost. Spremeni se vrstni red operacij. Vrata 587 se začnejo kot seja SMTP v čistem besedilu, v odgovoru EHLO oglašujejo STARTTLS in se nadgradijo le, če odjemalec to zahteva. Vrata 465 pričakujejo rokovanje TLS, preden je izgovorjen kateri koli ukaz SMTP.
Pošiljanje in relay sta različni opravili, zato številke vrat niso zamenljive navade. RFC 6409 opisuje pošiljanje sporočil kot samostojno storitev, RFC 8314 pa pokriva različico implicitnega TLS, ki jo večina sodobnih relayev ponuja na 465. Če vaš relay dokumentira enega od obeh, ima ta dokumentacija prednost pred katero koli preferenco iz prejšnje zaposlitve.
Kaj vam STARTTLS na 587 daje
Nadgradnja se zgodi znotraj pogovora SMTP, zato jo relay lahko oglašuje in odjemalec lahko zavrne nadaljevanje brez nje. V Pythonu starttls() vrže izjemo, ko strežnik te razširitve ne ponuja, kar poskus znižanja spremeni v vidno napako namesto tihe prijave v čistem besedilu. To vedenje je glavni argument za ta način in nanj se je vredno zanašati namerno, ne po naključju.
Okno izpostavljenosti je majhno, a resnično: začetna izmenjava EHLO je berljiva in nič pred nadgradnjo ne prenaša poverilnic. Na zasebni poti med podomrežjem aplikacije in notranjim relayem je to okno običajno sprejemljivo. Na poti, ki je ne nadzorujete, je prav to razlog, da nekatere ekipe vztrajajo pri drugem načinu.
Naprava za prestrezanje, ki odstrani zmožnost STARTTLS, ustvari neuspešno pošiljanje, ne vrnitve. To je rezultat, ki ga želite, in hkrati najhitrejši način, da odkrijete, da nekaj na poti prepisuje SMTP.
Kaj spremeni implicitni TLS na 465
Faze čistega besedila ni prav nobene. Odjemalec izvede rokovanje TLS takoj, ko se vtičnica odpre, preveri potrdilo v svojem skladišču zaupanja in šele nato izmenja EHLO, AUTH in sporočilo. Nič ne more znižati seje, ker ni stanja čistega besedila, iz katerega bi se dalo pogajati, in to odpravi cel razred napak prestrezanja.
Cena je diagnostičnost. Relay, ki na 465 odgovori s čistim SMTP, ali razporejevalnik obremenitve, ki zaključi TLS z napačnim imenom potrdila, spodleti pri rokovanju in vam da napako SSL namesto poštne napake. Ko se to zgodi, je težava pod aplikacijo in nobeno sestavljanje sporočila je ne odpravi.
Ena funkcija za pošiljanje, dva prevoza
Koda se med načinoma komaj razlikuje, zato je treba izbiro narediti glede na politiko relaya in ne glede na udobje knjižnice. Najprej izvozite nastavitve, nato zaženite eno skripto, ki izbere prevoz glede na konfigurirana vrata.
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() preveri verigo in ime gostitelja, zato je treba zasebni CA namestiti v skladišče zaupanja, ne pa ga obiti z nepreverjenim kontekstom. send_message() vrne slovar prejemnikov, ki jih je relay zavrnil; prazen slovar pomeni, da je sprejel vse. Petnajstsekundna časovna omejitev je izhodišče, ne vrednost, prilagojena vašemu omrežju.
Geslo hranite zunaj slike in zunaj dnevnikov. Skrivnost, nameščena med izvajanjem in prebrana v okolje, je v redu; odmevanje okolja v vrstico dnevnika ni, prav tako ne sled za razhroščevanje izmenjave AUTH.
Izberite po politiki relaya in poti, ne po navadi
Odločajo trije pogoji. Prvič, kaj relay v resnici oglašuje: storitev, ki posluša le na 587 s STARTTLS, ni dosegljiva na 465, ne glede na to, kako radi imate implicitni TLS. Drugič, ali je pot med aplikacijo in relayem dovolj zaupanja vredna, da sprejme berljivo izmenjavo EHLO. Tretjič, ali nekaj vmes prepisuje ali posreduje SMTP, ker to odloča, ali je tveganje znižanja teoretično ali dejansko.
Ko relay ponuja oboje, vzamem 465 za storitev, ki teče brez nadzora, in 587 za tisto, kjer človek opazuje prvo uvedbo, samo zato, ker sporočila o napakah kažejo na različne ravni. Ta preferenca je moja in ni politika.
Karkoli izberete, zabeležite vrata, gradivo zaupanja in domeno pošiljatelja skupaj z uvedbo. Nato pošljite eno pooblaščeno testno sporočilo in potrdite, da ga je relay sprejel, pri čemer rezultat pošiljanja in morebitno poznejše poročilo o dostavi hranite kot ločene zapise. Uspešna predaja relayu je konec odgovornosti aplikacije, ne dokaz, da je sporočilo prispelo v nabiralnik.