Python SMTP gönderimi: 587'de STARTTLS mi yoksa 465'te TLS mi?
587'de STARTTLS ve 465'te örtük TLS, ikisi de bir Python gönderimini taşır, ancak relay'inize yalnızca biri uyar. El sıkışmayı ve hata biçimlerini karşılaştırın.
Her iki bağlantı kipi de aynı iletileri aynı relay hesabına taşır ve fark, neyin gönderildiğinde değil, TLS'in nerede başladığındadır. Biri açık metinle açılır ve bağlantı içinde yükseltilir; diğeri ilk bayttan itibaren şifrelenir. Bu tek fark, katı bir relay ilkesinin neyi kabul edeceğini, bir ara cihazın neyi gözlemleyebileceğini ve hangi hata iletisini bir öğleden sonra okuyarak geçireceğinizi belirler. Özel bir ağdaki bir uygulama, bir iletiyi iç gönderim hizmetine teslim ettiğinde ikisi arasında nasıl seçim yaptığımı anlatıyorum.
İki kip gerçekte nerede ayrışır
Görev her iki durumda da aynıdır: onaylı bir hesap olarak kimlik doğrulamak, izin verilen bir gönderici belirtmek, alıcıları listelemek ve relay'in sorumluluğu üstlenmesini beklemek. Değişen, işlemlerin sırasıdır. 587 numaralı bağlantı noktası açık metin bir SMTP oturumu olarak başlar, EHLO yanıtında STARTTLS duyurur ve yalnızca istemci talep ederse yükseltilir. 465 numaralı bağlantı noktası ise herhangi bir SMTP komutu söylenmeden önce bir TLS el sıkışması bekler.
Gönderim ve relay farklı işlerdir, bu yüzden bağlantı noktası numaraları birbirinin yerine geçen alışkanlıklar değildir. RFC 6409 ileti gönderimini kendi başına bir hizmet olarak tanımlar ve RFC 8314, çoğu çağdaş relay'in 465'te sunduğu örtük TLS varyantını kapsar. Relay'iniz ikisinden birini belgeliyorsa, o belge önceki bir işverenden edindiğiniz herhangi bir tercihin önüne geçer.
587'de STARTTLS size ne kazandırır
Yükseltme SMTP görüşmesinin içinde gerçekleşir, böylece relay bunu duyurabilir ve istemci o olmadan devam etmeyi reddedebilir. Python'da, sunucu bu uzantıyı sunmadığında starttls() bir istisna fırlatır; bu da düşürme girişimini sessiz bir açık metin oturum açma yerine görünür bir hataya dönüştürür. Bu davranış bu kipin ana argümanıdır ve ona kazara değil bilinçli olarak güvenmeye değer.
Maruziyet penceresi küçük ama gerçektir: ilk EHLO alışverişi okunabilir ve yükseltmeden önce hiçbir şey kimlik bilgisi taşımaz. Bir uygulama alt ağı ile iç relay arasındaki özel bir yolda bu pencere genellikle kabul edilebilir. Denetlemediğiniz bir yolda ise bazı ekiplerin diğer kipte ısrar etmesinin nedeni tam olarak budur.
STARTTLS yeteneğini kaldıran bir araya girme cihazı, geri dönüş değil başarısız bir gönderim üretir. İstediğiniz sonuç budur ve yolda bir şeyin SMTP'yi yeniden yazdığını keşfetmenin en hızlı yolu da budur.
465'te örtük TLS neyi değiştirir
Hiçbir açık metin aşaması yoktur. İstemci soket açılır açılmaz TLS el sıkışması yapar, sertifikayı kendi güven deposuna karşı doğrular ve ancak ondan sonra EHLO, AUTH ve iletiyi değiş tokuş eder. Müzakere edilecek bir açık metin durumu olmadığı için hiçbir şey oturumu düşüremez ve bu, araya girme hatalarının bütün bir sınıfını ortadan kaldırır.
Karşılığında elde edilen tanılanabilirliktir. 465'te açık metin SMTP ile yanıt veren bir relay ya da TLS'i yanlış sertifika adıyla sonlandıran bir yük dengeleyici el sıkışmada başarısız olur ve size posta hatası yerine SSL hatası verir. Bu olduğunda sorun uygulamanın altındadır ve ne kadar ileti kurarsanız kurun bunu düzeltmez.
Tek gönderim işlevi, iki taşıma
Kod kipler arasında neredeyse hiç farklılaşmaz, bu yüzden seçim kütüphanenin kolaylığından değil relay ilkesinden yapılmalıdır. Önce ayarları dışa aktarın, sonra yapılandırılmış bağlantı noktasına göre taşımayı seçen tek bir betik çalıştırın.
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() zinciri ve ana bilgisayar adını doğrular, bu yüzden özel bir CA güven deposuna kurulmalı, doğrulanmamış bir bağlamla atlatılmamalıdır. send_message() relay'in reddettiği alıcıların bir sözlüğünü döndürür; boş sözlük hepsini kabul ettiği anlamına gelir. On beş saniyelik zaman aşımı bir başlangıç noktasıdır, ağınız için ayarlanmış bir değer değildir.
Parolayı imajın ve günlüklerin dışında tutun. Çalışma zamanında bağlanan ve ortama okunan bir sır uygundur; ortamı bir günlük satırına yansıtmak uygun değildir, AUTH alışverişinin hata ayıklama izi de öyle değildir.
Alışkanlığa göre değil, relay ilkesine ve yola göre seçin
Üç koşul belirler. Birincisi, relay'in gerçekte ne duyurduğu: yalnızca 587'de STARTTLS ile dinleyen bir hizmete 465'te ulaşılamaz, örtük TLS'i ne kadar tercih etseniz de. İkincisi, uygulama ile relay arasındaki yolun okunabilir bir EHLO alışverişini kabul edecek kadar güvenilir olup olmadığı. Üçüncüsü, arada bir şeyin SMTP'yi yeniden yazıp yazmadığı ya da proxy'leyip proxy'lemediği; çünkü bu, düşürme riskinin teorik mi yoksa gerçek mi olduğunu belirler.
Relay ikisini de sunduğunda, denetimsiz çalışan bir hizmet için 465'i, ilk dağıtımı bir kişinin izlediği hizmet için 587'yi seçiyorum; yalnızca hata iletileri farklı katmanlara işaret ettiği için. Bu tercih benim ve bir ilke değil.
Ne seçerseniz seçin, bağlantı noktasını, güven malzemesini ve gönderici alan adını dağıtımla birlikte kaydedin. Ardından yetkilendirilmiş bir test iletisi gönderin ve relay'in onu kabul ettiğini doğrulayın; gönderim sonucunu ve sonraki herhangi bir teslimat raporunu ayrı kayıtlar olarak saklayın. Relay'e başarılı teslim, uygulamanın sorumluluğunun sonudur, iletinin gelen kutusuna ulaştığının kanıtı değildir.