Υποβολή μηνυμάτων Python μέσω SMTP: STARTTLS στο 587 ή TLS στο 465;
Το STARTTLS στο 587 και το έμμεσο TLS στο 465 μεταφέρουν και τα δύο μια υποβολή από Python, αλλά μόνο ένα ταιριάζει με το relay σας. Συγκρίνετε τη χειραψία και τους τρόπους αποτυχίας.
Και οι δύο τρόποι σύνδεσης μεταφέρουν το ίδιο μήνυμα στον ίδιο λογαριασμό relay, και η διαφορά είναι πού αρχίζει το TLS, όχι τι αποστέλλεται. Ο ένας ανοίγει σε απλό κείμενο και αναβαθμίζεται εντός της σύνδεσης· ο άλλος είναι κρυπτογραφημένος από το πρώτο byte. Αυτή η μία διαφορά καθορίζει τι θα δεχτεί μια αυστηρή πολιτική relay, τι μπορεί να παρατηρήσει μια ενδιάμεση συσκευή και ποιο μήνυμα σφάλματος θα περάσετε ένα απόγευμα διαβάζοντας. Έτσι επιλέγω ανάμεσά τους όταν μια εφαρμογή σε ιδιωτικό δίκτυο παραδίδει ένα μήνυμα σε εσωτερική υπηρεσία υποβολής.
Πού πραγματικά αποκλίνουν οι δύο τρόποι
Η εργασία είναι ίδια και στις δύο περιπτώσεις: έλεγχος ταυτότητας ως εγκεκριμένος λογαριασμός, δήλωση επιτρεπόμενου αποστολέα, καταγραφή παραληπτών και αναμονή για να αναλάβει το relay την ευθύνη. Αυτό που αλλάζει είναι η σειρά των ενεργειών. Η θύρα 587 ξεκινά ως συνεδρία SMTP σε απλό κείμενο, διαφημίζει το STARTTLS στην απόκριση EHLO και αναβαθμίζεται μόνο αν το ζητήσει ο πελάτης. Η θύρα 465 αναμένει χειραψία TLS πριν ειπωθεί οποιαδήποτε εντολή SMTP.
Η υποβολή και το relay είναι διαφορετικές εργασίες, γι’ αυτό οι αριθμοί θυρών δεν είναι ανταλλάξιμες συνήθειες. Το RFC 6409 περιγράφει την υποβολή μηνυμάτων ως ξεχωριστή υπηρεσία, και το RFC 8314 καλύπτει την παραλλαγή έμμεσου TLS που εκθέτουν τα περισσότερα σύγχρονα relay στη θύρα 465. Αν το relay σας τεκμηριώνει ένα από τα δύο, αυτή η τεκμηρίωση υπερισχύει οποιασδήποτε προτίμησης αποκτήσατε από προηγούμενο εργοδότη.
Τι σας δίνει το STARTTLS στη θύρα 587
Η αναβάθμιση γίνεται μέσα στη συνομιλία SMTP, ώστε το relay να μπορεί να τη διαφημίσει και ο πελάτης να μπορεί να αρνηθεί να συνεχίσει χωρίς αυτήν. Στην Python, το starttls() πετά εξαίρεση όταν ο διακομιστής δεν προσφέρει την επέκταση, κάτι που μετατρέπει μια απόπειρα υποβάθμισης σε ορατή αποτυχία αντί για σιωπηλή σύνδεση σε απλό κείμενο. Αυτή η συμπεριφορά είναι το κύριο επιχείρημα υπέρ του τρόπου, και αξίζει να βασίζεστε σε αυτήν σκόπιμα και όχι τυχαία.
Το παράθυρο έκθεσης είναι μικρό αλλά πραγματικό: η αρχική ανταλλαγή EHLO είναι αναγνώσιμη, και τίποτα πριν από την αναβάθμιση δεν μεταφέρει διαπιστευτήρια. Σε ιδιωτική διαδρομή μεταξύ υποδικτύου εφαρμογής και εσωτερικού relay, αυτό το παράθυρο είναι συνήθως αποδεκτό. Σε διαδρομή που δεν ελέγχετε, είναι ο λόγος που ορισμένες ομάδες επιμένουν στον άλλο τρόπο.
Μια συσκευή υποκλοπής που αφαιρεί τη δυνατότητα STARTTLS παράγει αποτυχημένη υποβολή, όχι υποχώρηση. Αυτό είναι το αποτέλεσμα που θέλετε, και είναι επίσης ο ταχύτερος τρόπος να ανακαλύψετε ότι κάτι ξαναγράφει το SMTP στη διαδρομή.
Τι αλλάζει το έμμεσο TLS στη θύρα 465
Δεν υπάρχει καθόλου φάση απλού κειμένου. Ο πελάτης εκτελεί χειραψία TLS μόλις ανοίξει ο υποδοχέας, επικυρώνει το πιστοποιητικό έναντι του χώρου έμπιστων πιστοποιητικών του και μόνο τότε ανταλλάσσει EHLO, AUTH και το μήνυμα. Τίποτα δεν μπορεί να υποβαθμίσει τη συνεδρία επειδή δεν υπάρχει κατάσταση απλού κειμένου από την οποία να διαπραγματευτεί, και αυτό αφαιρεί μια ολόκληρη κατηγορία αποτυχιών υποκλοπής.
Το αντάλλαγμα είναι η δυνατότητα διάγνωσης. Ένα relay που απαντά στη θύρα 465 με απλό SMTP, ή ένας εξισορροπητής φόρτου που τερματίζει το TLS με λάθος όνομα πιστοποιητικού, αποτυγχάνει στη χειραψία και σας δίνει σφάλμα SSL αντί για σφάλμα αλληλογραφίας. Όταν συμβεί αυτό, το πρόβλημα βρίσκεται κάτω από την εφαρμογή, και κανένα χτίσιμο μηνύματος δεν θα το διορθώσει.
Μία συνάρτηση υποβολής, δύο μεταφορές
Ο κώδικας δύσκολα διαφέρει μεταξύ των τρόπων, γι’ αυτό η επιλογή πρέπει να γίνεται με βάση την πολιτική του relay και όχι την ευκολία της βιβλιοθήκης. Εξαγάγετε πρώτα τις ρυθμίσεις και μετά εκτελέστε ένα μόνο σενάριο που επιλέγει τη μεταφορά από τη ρυθμισμένη θύρα.
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() επαληθεύει την αλυσίδα και το όνομα κεντρικού υπολογιστή, οπότε μια ιδιωτική CA πρέπει να εγκατασταθεί στον χώρο έμπιστων πιστοποιητικών αντί να παρακαμφθεί με μη επαληθευμένο περιβάλλον. Το send_message() επιστρέφει ένα λεξικό παραληπτών που απέρριψε το relay· ένα κενό λεξικό σημαίνει ότι τους δέχτηκε όλους. Το χρονικό όριο των δεκαπέντε δευτερολέπτων είναι σημείο εκκίνησης, όχι συντονισμένη τιμή για το δίκτυό σας.
Κρατήστε τον κωδικό πρόσβασης έξω από την εικόνα και έξω από τα αρχεία καταγραφής. Ένα μυστικό που προσαρτάται κατά την εκτέλεση και διαβάζεται στο περιβάλλον είναι εντάξει· η αναπαραγωγή του περιβάλλοντος σε γραμμή καταγραφής δεν είναι, ούτε ένα ίχνος αποσφαλμάτωσης της ανταλλαγής AUTH.
Επιλέξτε βάσει πολιτικής relay και διαδρομής, όχι βάσει συνήθειας
Τρεις συνθήκες το καθορίζουν. Πρώτον, τι διαφημίζει πραγματικά το relay: μια υπηρεσία που ακούει μόνο στη θύρα 587 με STARTTLS δεν είναι προσβάσιμη στη θύρα 465, όσο κι αν προτιμάτε το έμμεσο TLS. Δεύτερον, αν η διαδρομή μεταξύ εφαρμογής και relay είναι αρκετά έμπιστη ώστε να δεχτεί αναγνώσιμη ανταλλαγή EHLO. Τρίτον, αν κάτι στη μέση ξαναγράφει ή κάνει proxy το SMTP, γιατί αυτό καθορίζει αν ο κίνδυνος υποβάθμισης είναι θεωρητικός ή ζωντανός.
Όταν το relay προσφέρει και τα δύο, παίρνω το 465 για μια υπηρεσία που τρέχει χωρίς επίβλεψη και το 587 για μία όπου ένα άτομο παρακολουθεί την πρώτη ανάπτυξη, μόνο και μόνο επειδή τα μηνύματα αποτυχίας δείχνουν σε διαφορετικά επίπεδα. Αυτή η προτίμηση είναι δική μου και δεν είναι πολιτική.
Ό,τι κι αν επιλέξετε, καταγράψτε τη θύρα, το υλικό έμπιστων πιστοποιητικών και τον τομέα αποστολέα μαζί με την ανάπτυξη. Στη συνέχεια στείλτε ένα εξουσιοδοτημένο δοκιμαστικό μήνυμα και επιβεβαιώστε ότι το relay το δέχτηκε, κρατώντας το αποτέλεσμα της υποβολής και κάθε μεταγενέστερη αναφορά παράδοσης ως ξεχωριστές εγγραφές. Μια επιτυχημένη παράδοση στο relay είναι το τέλος της ευθύνης της εφαρμογής, όχι απόδειξη ότι το μήνυμα έφτασε σε γραμματοκιβώτιο.