Pythonin SMTP-lähetys: STARTTLS portissa 587 vai TLS portissa 465?
STARTTLS portissa 587 ja implisiittinen TLS portissa 465 kuljettavat molemmat Python-lähetyksen, mutta vain yksi sopii välittäjääsi. Vertaa kättelyä ja virhetiloja.
Molemmat yhteystavat kuljettavat saman viestin samalle välittäjätilille, ja ero on siinä, mistä TLS alkaa, ei siinä, mitä lähetetään. Toinen avautuu selväkielisenä ja päivittyy kaistan sisällä; toinen on salattu ensimmäisestä tavusta lähtien. Tämä yksi ero ratkaisee, mitä tiukka välittäjäkäytäntö hyväksyy, mitä välissä oleva laite voi havaita ja mitä virheilmoitusta vietät iltapäivän lukien. Näin valitsen niiden välillä, kun yksityisverkossa oleva sovellus luovuttaa viestin sisäiselle lähetyspalvelulle.
Missä kaksi tapaa todella eroavat
Tehtävä on molemmissa tapauksissa sama: tunnistaudu hyväksyttynä tilinä, nimeä sallittu lähettäjä, luettele vastaanottajat ja odota, että välittäjä ottaa vastuun. Muuttuu toimintojen järjestys. Portti 587 alkaa selväkielisenä SMTP-istuntona, ilmoittaa STARTTLSin EHLO-vastauksessa ja päivittyy vain, jos asiakas pyytää. Portti 465 odottaa TLS-kättelyä ennen kuin yhtään SMTP-komentoa sanotaan.
Lähetys ja välitys ovat eri töitä, minkä vuoksi porttinumerot eivät ole vaihdettavia tapoja. RFC 6409 kuvaa viestien lähetyksen omana palvelunaan, ja RFC 8314 kattaa implisiittisen TLS:n muunnelman, jonka useimmat nykyaikaiset välittäjät tarjoavat portissa 465. Jos välittäjäsi dokumentoi toisen näistä kahdesta, se dokumentaatio menee kaiken edelle, mitä olet oppinut aiemmalta työnantajalta.
Mitä STARTTLS portissa 587 antaa sinulle
Päivitys tapahtuu SMTP-keskustelun sisällä, joten välittäjä voi ilmoittaa sen ja asiakas voi kieltäytyä jatkamasta ilman sitä. Pythonissa starttls() heittää poikkeuksen, kun palvelin ei tarjoa laajennusta, mikä muuttaa alentamisyrityksen näkyväksi epäonnistumiseksi hiljaisen selväkielisen kirjautumisen sijaan. Tämä käyttäytyminen on tavan pääargumentti, ja siihen kannattaa luottaa tarkoituksella eikä vahingossa.
Altistumisikkuna on pieni mutta todellinen: alkuperäinen EHLO-vaihto on luettavissa, eikä mikään ennen päivitystä kuljeta tunnistetietoja. Yksityisellä polulla sovelluksen aliverkon ja sisäisen välittäjän välillä tämä ikkuna on yleensä hyväksyttävä. Polulla, jota et hallitse, juuri se on syy, miksi jotkin tiimit vaativat toista tapaa.
Sieppauslaite, joka poistaa STARTTLS-ominaisuuden, tuottaa epäonnistuneen lähetyksen, ei paluuta. Se on toivottu lopputulos, ja se on myös nopein tapa havaita, että jokin kirjoittaa SMTP:tä uudelleen polulla.
Mitä implisiittinen TLS portissa 465 muuttaa
Selväkielistä vaihetta ei ole lainkaan. Asiakas tekee TLS-kättelyn heti, kun pistoke avautuu, validoi varmenteen luottamussäilöään vasten ja vaihtaa vasta sitten EHLO:n, AUTHin ja viestin. Mikään ei voi alentaa istuntoa, koska selväkielistä tilaa ei ole neuvoteltavana, ja se poistaa kokonaisen sieppausvirheiden luokan.
Kaupan hinta on diagnosoitavuus. Välittäjä, joka vastaa portissa 465 selväkielisellä SMTP:llä, tai kuormantasaaja, joka päättää TLS:n väärällä varmenteen nimellä, epäonnistuu kättelyssä ja antaa sinulle SSL-virheen postivirheen sijaan. Kun niin käy, ongelma on sovelluksen alapuolella, eikä mikään viestin rakentaminen korjaa sitä.
Yksi lähetystoiminto, kaksi kuljetusta
Koodi tuskin eroaa tapojen välillä, joten valinta pitäisi tehdä välittäjäkäytännöstä eikä kirjaston mukavuudesta. Vie asetukset ensin ja aja sitten yksi skripti, joka valitsee kuljetuksen määritetyn portin perusteella.
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() tarkistaa ketjun ja isäntänimen, joten yksityinen CA on asennettava luottamussäilöön sen sijaan, että se ohitettaisiin vahvistamattomalla kontekstilla. send_message() palauttaa sanakirjan vastaanottajista, jotka välittäjä hylkäsi; tyhjä sanakirja tarkoittaa, että se hyväksyi kaikki. Viidentoista sekunnin aikakatkaisu on lähtökohta, ei verkkollesi viritetty arvo.
Pidä salasana poissa imagesta ja lokeista. Ajonaikaisesti liitetty ja ympäristöön luettu salaisuus on kunnossa; ympäristön kaikuminen lokiriville ei ole, eikä AUTH-vaihdon virheenjäljitys ole sitä myöskään.
Valitse välittäjäkäytännön ja polun mukaan, älä tottumuksen
Kolme ehtoa ratkaisee sen. Ensinnäkin, mitä välittäjä todella ilmoittaa: palvelu, joka kuuntelee vain portissa 587 STARTTLSin kanssa, ei ole saavutettavissa portissa 465, kuinka paljon tahansa suosit implisiittistä TLS:ää. Toiseksi, onko sovelluksen ja välittäjän välinen polku tarpeeksi luotettu hyväksymään luettava EHLO-vaihto. Kolmanneksi, kirjoittaako tai välityspalveluttaako jokin välissä SMTP:tä, koska se ratkaisee, onko alentamisriski teoreettinen vai todellinen.
Kun välittäjä tarjoaa molemmat, otan 465:n valvomatta ajettavalle palvelulle ja 587:n sellaiselle, jossa ihminen seuraa ensimmäistä käyttöönottoa, vain siksi, että virheilmoitukset osoittavat eri kerroksiin. Tämä mieltymys on minun eikä se ole käytäntö.
Minkä tahansa valitsetkin, kirjaa portti, luottamusmateriaali ja lähettäjän verkkotunnus käyttöönoton yhteyteen. Lähetä sitten yksi valtuutettu testiviesti ja vahvista, että välittäjä hyväksyi sen, pitäen lähetystuloksen ja mahdollisen myöhemmän toimitusraportin erillisinä tietueina. Onnistunut luovutus välittäjälle on sovelluksen vastuun loppu, ei todiste siitä, että viesti saapui postilaatikkoon.