Порт 587 проти 465: STARTTLS чи implicit TLS і що обрати у 2026
Лист «не йде», хоча логін і пароль правильні? У 90% випадків це плутанина між submission-портами й трьома режимами шифрування. Розбираємо хендшейк у сокеті, атаку STARTTLS stripping, хронологію RFC і даємо готові конфіги для Nodemailer, Python і Postfix.
EvilMail Team20 липня 2026 р.11 хв читання
Класична сцена: код готовий, у налаштуваннях host, port, логін і пароль з панелі — усе правильне, а лист не йде. У логах або таймаут, або wrong version number, або він таки «відправляється», але кладе credentials відкритим текстом у мережу. Майже завжди корінь один: розробник плутає три різні порти й три різні режими шифрування та обирає їх наосліп.
Суперечка «587 проти 465» досі жива тільки тому, що половина туторіалів застрягла у 2010 році з мантрою «завжди 587, бо STARTTLS — це стандарт». З 2018 року це неправда. Розберемо, що реально відбувається в сокеті, чому implicit TLS безпечніший за дизайном, і дамо конфіги, які працюють.
Три порти, які постійно плутають
Спершу треба розвести дві зовсім різні задачі, що випадково користуються одним протоколом SMTP.
Transport (порт 25)
— це relay між поштовими серверами, MX-to-MX. Коли ваш сервер приймає лист і передає його на сервер отримувача, він стукає у порт 25. Тут немає автентифікації логіном/паролем, шифрування опортуністичне, і — головне —
порт 25 на вихід заблокований у всіх нормальних хмарах
: AWS, GCP, Azure, DigitalOcean ріжуть його за замовчуванням, щоб не хостити спамерів. Якщо ви відправляєте лист із застосунку через порт 25, ви робите це неправильно.
Submission (порти 587 і 465) — це клієнт → сервер із обов'язковою автентифікацією. Саме сюди підключається Thunderbird, ваш бекенд на Node.js чи Python, ваш msmtp. Різниця між 587 і 465 — виключно в тому, коли й як вмикається TLS.
STARTTLS vs implicit TLS: що реально в сокеті
Обидва порти врешті дають зашифрований канал. Питання лише в тому, скільки байтів пролетить відкритим текстом до того, як шифрування ввімкнеться.
Implicit TLS (465). Одразу після TCP-connect клієнт починає TLS-хендшейк. Банер сервера 220 mail.evilmail.pro ESMTP приходить уже всередині зашифрованого каналу. EHLO, AUTH LOGIN, тіло листа — усе під шифром від першого байта. Plaintext-вікна не існує в принципі.
STARTTLS (587). Клієнт відкриває звичайний, незашифрований TCP-конект. Сервер відповідає 220 відкритим текстом. Клієнт шле EHLO, сервер анонсує можливості, серед яких рядок 250-STARTTLS. Тільки тоді клієнт відправляє команду STARTTLS, отримує 220 Ready to start TLS і апгрейдить з'єднання. Перші три-чотири обміни — plaintext. І саме там живе проблема.
STARTTLS stripping — головний аргумент проти «587 за замовчуванням»
Ось конкретний сценарій. Між вашим клієнтом і сервером сидить активний посередник — скомпрометований роутер, ворожий Wi-Fi, оператор, який «оптимізує трафік». Клієнт шле EHLO, сервер відповідає списком можливостей із рядком 250-STARTTLS. Посередник вирізає цей рядок з відповіді. Клієнт бачить сервер, який нібито не вміє TLS.
І тут усе вирішує налаштування клієнта. Якщо він на *opportunistic TLS* («шифруй, якщо можна»), він знизує плечима й продовжує відкритим текстом: шле AUTH LOGIN, а разом із ним логін і пароль. Деталь, яку багато хто пропускає: AUTH LOGIN і AUTH PLAIN передають credentials у base64. Base64 — це кодування, а не шифрування. Рядок dXNlckBleGFtcGxlLmNvbQ== розкручується назад у [email protected] однією командою. Без TLS ваш пароль летить мережею фактично відкритим.
Це не теоретична страшилка. Заміри Google по SMTP-трафіку ще у 2015 фіксували цілі мережі, де STARTTLS системно вирізався з відповідей. Провайдери, які «економлять» на TLS, нікуди не зникли.
Захиститися на 587 можна, але ціною цілого списку обов'язкових речей: mandatory TLS у клієнті (requireTLS, а не opportunistic), MTA-STS (RFC 8461) для політики «тільки TLS», DANE/TLSA (RFC 7672) через DNSSEC, TLS-RPT (RFC 8460) для звітів про збої. Кожен пункт — це ще одна річ, яку треба правильно налаштувати й не зламати.
Implicit TLS на 465 прибирає весь цей клас атак на рівні дизайну. Немає plaintext-фази — немає чого вирізати. Downgrade не має точки входу: посередник, який лізе в потік, ламає сам TLS-хендшейк, і з'єднання просто обривається замість того, щоб тихо відкотитись у відкритий текст.
Що каже RFC: хронологія, яка все ставить на місце
Міф «465 застарілий» походить із реальної, але давно завершеної історії.
RFC 2476 (грудень 1998) уперше ввів окремий submission-порт 587, щоб розвести клієнтський сабміт і MTA-relay на порту 25.
Порт 465 IANA спершу видала під SMTPS (implicit TLS), потім відкликала реєстрацію і на роки віддала його під зовсім інший сервіс («urd»). Звідси й пішло «465 — deprecated».
RFC 6409 (2011) оновив і закріпив submission на 587 зі STARTTLS.
RFC 8314 (січень 2018) закрив питання остаточно. Він офіційно повернув порт 465 під назвою «submissions» (з s на кінці, implicit TLS) і прямим текстом рекомендує надавати перевагу implicit TLS над STARTTLS для сабміту пошти.
Тобто станом на 2026 рік актуальний стандарт каже: 465 — це не legacy, це рекомендований варіант. Той, хто відповідає «завжди 587», цитує документ, який чинна RFC переглянула вісім років тому.
Як обрати: для клієнта і для застосунку
Рішення без сидіння на паркані.
Поштові клієнти (Thunderbird, Apple Mail, Outlook): ставте 465, SSL/TLS, з перевіркою сертифіката. 587 зі STARTTLS — лише як фолбек, якщо старий сервер не слухає 465.
Застосунки й бекенди: 465 з `secure: true` і ввімкненою валідацією сертифіката. 587 тримайте як запасний варіант — деякі корпоративні firewall ріжуть 465 (він рідше потрапляє в дефолтні allow-list), і тоді 587 рятує.
Порт 25 для сабміту з застосунку — ніколи. Це transport: він заблокований на вихід у хмарах і не автентифікує вас.
І одразу вбиваю поширену ілюзію: порт не впливає на те, чи потрапить лист в inbox чи в спам. За доставлюваність відповідають SPF, DKIM, DMARC, репутація IP та коректний rDNS/PTR. Зміна 587 на 465 не витягне вас зі спам-фолдера — це різні шари. Новачки регулярно крутять порти там, де треба крутити DNS.
Тут secure: false не означає «без шифрування» — воно означає «почни plaintext і піднімись через STARTTLS». А requireTLS: true забороняє opportunistic-відкат: якщо TLS не піднявся, Nodemailer розірве конект замість того, щоб слати пароль голим. Без цього прапорця 587 вразливий до stripping.
Це найпоширеніший «фікс» зі StackOverflow, який копіюють, щоб прибрати certificate verify failed. Він не лагодить проблему — він вимикає весь сенс TLS. Правильно — виправити сертифікат чи ланцюг, а не глушити перевірку.
Python, implicit TLS vs STARTTLS:
python
import smtplib, ssl
ctx = ssl.create_default_context() # перевірка hostname + CA з коробки
# 465 — implicit TLS
with smtplib.SMTP_SSL("mail.evilmail.pro", 465, context=ctx) as s:
s.login("[email protected]", PASS)
s.send_message(msg)
# 587 — STARTTLS
with smtplib.SMTP("mail.evilmail.pro", 587) as s:
s.starttls(context=ctx) # без context перевірки НЕ буде
s.login("[email protected]", PASS)
s.send_message(msg)
create_default_context() — критичний нюанс. Якщо викликати starttls() без нього, старіші збірки Python можуть не перевіряти сертифікат зовсім.
Postfix, обидва submission-порти в `master.cf`:
submission inet n - y - - smtpd
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
smtps inet n - y - - smtpd
-o smtpd_tls_wrappermode=yes
-o smtpd_sasl_auth_enable=yes
smtpd_tls_security_level=encrypt на 587 робить STARTTLS обов'язковим — сервер не пустить без TLS. smtpd_tls_wrappermode=yes на 465 вмикає implicit TLS.
Діагностика: як довести, що саме ламається
Не гадайте — під'єднайтесь руками через openssl і подивіться, що каже сервер.
bash
# implicit TLS (465): TLS одразу, банер приходить уже під шифром
openssl s_client -connect mail.evilmail.pro:465 -servername mail.evilmail.pro
# STARTTLS (587): openssl сам зробить апгрейд і покаже сертифікат
openssl s_client -starttls smtp -connect mail.evilmail.pro:587 -servername mail.evilmail.pro
Прапорець -servernameобов'язковий — це SNI. Без нього на сервері з кількома доменами ви отримаєте не той сертифікат і хибний certificate verify failed. Після конекту введіть EHLO test — на 587 у відповіді має бути рядок 250-STARTTLS; якщо його немає, а ви чекаєте STARTTLS, десь по дорозі його вирізали.
Розшифровка типових симптомів:
`wrong version number` — ви під'єдналися implicit-логікою (465-style) до STARTTLS-порту або навпаки. Клієнт чекає TLS, а сервер шле plaintext-банер 220. Перевірте пару port/secure.
`certificate verify failed` — self-signed сертифікат, відсутній SNI (-servername) або неповний ланцюг (сервер не віддає проміжний CA).
connection timeout на 465 — майже завжди firewall у мережі клієнта ріже implicit-submission. Це і є причина тримати 587 як фолбек.
Щоб переконатися, що на 587 нічого не тече відкритим текстом, tcpdump -A -i any port 587 покаже банер і EHLO у чистому вигляді до STARTTLS — це нормально. Але якщо ви бачите там AUTH LOGIN і base64 після нього, значить TLS не піднявся й пароль щойно засвітився.
Чеклист перед продакшеном
465 (implicit TLS) — дефолт для нових клієнтів і застосунків.
587 (STARTTLS) — фолбек на випадок firewall, який ріже 465.
25 — ніколи для сабміту з застосунку.
secure: true (Nodemailer) / SMTP_SSL (Python) на 465; requireTLS: true / starttls(context=...) на 587.
Перевірка сертифіката увімкнена; rejectUnauthorized: false прибрано з коду.
SNI виставлений (-servername, у бібліотеках — коректний host, а не IP).
Якщо STARTTLS неминучий — додайте MTA-STS і TLS-RPT у DNS.
SPF, DKIM, DMARC налаштовані: порт не рятує від спам-фолдера, за inbox відповідає автентифікація домену.
Моніторинг терміну дії сертифіката — прострочений TLS кладе відправку миттєво.
Тест обох портів у CI через openssl s_client, щоб зміна на сервері не зламала прод мовчки.
Коротко: беріть 465, вмикайте перевірку сертифіката, тримайте 587 напоготові і не чіпайте 25. Решту зусиль вкладайте в DNS-автентифікацію — саме вона вирішує, чи побачить отримувач ваш лист.