MTA-STS без DNSSEC: політика по HTTPS, DNS-індикатор і звіти TLS-RPT про збої шифрування
STARTTLS сам по собі не захищає нічого: активний посередник вирізає рядок з відповіді сервера — і ваша пошта йде відкритим текстом без жодного попередження. MTA-STS закриває цю діру без DNSSEC. Бойовий runbook: публікація політики по HTTPS, DNS-індикатор, збір JSON-звітів TLS-RPT і безпечна розкатка none → testing → enforce без втрати листів.
EvilMail Team13 липня 2026 р.11 хв читання
Чому STARTTLS не захищає нічого сам по собі
Уявіть шлях листа між двома поштовими серверами. Ваш MTA підключається до чужого MX на 25 порт, бачить у відповіді на EHLO рядок 250 STARTTLS, піднімає TLS і спокійно віддає пошту зашифрованою. Так виглядає щасливий сценарій.
Тепер додайте активного посередника — провайдера на транзиті, скомпрометований роутер, будь-кого, хто сидить між двома MTA. Йому не треба ламати шифрування. Йому достатньо вирізати вісім символів. Він переписує відповідь сервера так, ніби STARTTLS там ніколи не було:
Відправник не бачить STARTTLS — отже, за логікою опортуністичного TLS, робить висновок «сервер не вміє шифрувати» і доставляє лист
MTA-STS і TLS-RPT: налаштування політики шифрування пошти без DNSSEC — EvilMail Blog
відкритим текстом
. Жодної помилки, жодного попередження ні відправнику, ні одержувачу. Це класична downgrade-атака, і opportunistic STARTTLS проти неї беззахисний за дизайном.
Друга діра тонша. Навіть коли TLS піднявся, опортуністичний режим перевіряє факт шифрування, але не автентичність сертифіката. Self-signed, прострочений, виданий не на той хост — усе це проходить. Тобто активний MITM може просто підставити власний сертифікат, і трафік «зашифрується» прямо йому в руки.
DANE вирішує обидві проблеми: публікуєте TLSA-запис, і відправник знає точний відбиток вашого сертифіката. Але DANE вимагає DNSSEC на всій ланцюжку зон, а в українському сегменті DNSSEC досі радше виняток, ніж норма — багато реєстраторів або не підтримують підпис зони, або роблять це криво. MTA-STS обходить цю вимогу повністю: домен публікує політику по HTTPS, підписану звичайним валідним TLS-сертифікатом від публічного CA, а великі відправники кешують її й відмовляються доставляти пошту, якщо TLS не піднявся або сертифікат не збігається. Хто це enforce на практиці у 2026: Gmail і Google Workspace, Microsoft 365 / Outlook, Yahoo. Тобто це не теорія — це поведінка серверів, які надсилають більшість вхідної пошти на ваш домен.
Три рухомі частини MTA-STS
MTA-STS (RFC 8461) складається з трьох окремих механізмів, і плутанина між ними — джерело половини помилок налаштування.
DNS TXT-індикатор_mta-sts.<домен> — дешевий сигнал «політика існує, ось її версія». Він не містить самої політики, лише поле id.
Файл політики по HTTPS на піддомені mta-sts.<домен> — джерело правди зі списком MX, режимом і TTL кешу.
Кеш на боці відправника — відправник тримає політику max_age секунд і не перезапитує її щоразу.
Ключова деталь механіки: відправник спершу читає TXT-запис (це дешево, один DNS-запит), дивиться на id і порівнює його з тим, що лежить у кеші. Якщо id не змінився — політика береться з кешу, HTTPS-запит не робиться взагалі. І лише якщо id новий — відправник іде по HTTPS тягнути свіжий файл політики. Саме тому id — це версія кешу, а не декоративне поле.
І тут головна вимога, яку легко проґавити: сертифікат на mta-sts.<домен> має бути валідним, довіреним і не простроченим. Self-signed не приймається — на відміну від опортуністичного STARTTLS, тут довіра прив'язана до публічних CA. І відправник MUST NOT слідувати HTTP-редиректам при завантаженні .well-known/mta-sts.txt. Якщо ваш веб-фронт робить 301 з http на https або зі старого домену — політика просто не завантажиться.
Публікація політики по HTTPS
Починаємо з піддомену. Створюємо mta-sts.evilmail.pro з A-записом на веб-фронт (за наявності IPv6 — додаємо AAAA):
text
mta-sts.evilmail.pro. IN A 203.0.113.10
Випускаємо для нього сертифікат — звичайний Let's Encrypt цілком годиться, головне щоб CN/SAN дорівнював mta-sts.evilmail.pro. Далі кладемо файл політики. Він має віддаватися рівно за адресою /.well-known/mta-sts.txt з Content-Type: text/plain, з LF-переносами рядків (не CRLF — деякі парсери спотикаються об \r\n):
nginx-блок мінімальний, але з однією критичною вимогою — жодних редиректів на цьому location:
nginx
server {
listen 443 ssl;
server_name mta-sts.evilmail.pro;
ssl_certificate /etc/letsencrypt/live/mta-sts.evilmail.pro/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mta-sts.evilmail.pro/privkey.pem;
location = /.well-known/mta-sts.txt {
default_type text/plain;
add_header Content-Type "text/plain; charset=utf-8";
alias /var/www/mta-sts/mta-sts.txt;
}
# важливо: жодних 301/302 на цей location
}
Поля політики розшифровуються так. version завжди STSv1. mode — none, testing або enforce. mx — список приймаючих серверів; можна wildcard *.evilmail.pro, можна перелічити кілька рядків. max_age — скільки секунд відправник тримає політику в кеші.
Найпоширеніша пастка тут — розбіжність MX. Значення в полях mx: мають точно збігатися з тим, що віддає ваш MX-запис у DNS, і з CN/SAN сертифікатів на приймаючих серверах. Якщо MX-запис вказує на mail.evilmail.pro, а сертифікат на тому сервері виданий на mx1.hosting-provider.net — на enforce ви отримаєте certificate-host-mismatch і пошта стане.
DNS-індикатор і версіонування через id
Тепер публікуємо TXT-індикатор:
text
_mta-sts.evilmail.pro. IN TXT "v=STSv1; id=20260704T120000"
id — це версія кешу, і поводитися з ним треба відповідно. Конвенція — timestamp формату YYYYMMDDThhmmss. Правило одне, але залізне: щоразу, коли ви змінюєте вміст файлу політики — додали MX, підняли max_age, перемкнули mode — ви зобов'язані змінити id. Інакше відправники, які вже закешували стару політику, тижнями сидітимуть на ній і навіть не зазирнуть у ваш оновлений mta-sts.txt.
І зворотний бік: не чіпайте id, якщо вміст політики не змінився. Передеплой того самого файлу, рестарт nginx, зміна коментаря — це не привід крутити версію. Зайва зміна id змушує всіх відправників дарма перезавантажувати політику по HTTPS.
TLS-RPT: збирайте звіти ПЕРШ ніж вмикати enforce
Ось запис, який рятує вас від самострілу:
text
_smtp._tls.evilmail.pro. IN TXT "v=TLSRPTv1; rua=mailto:[email protected]"
TLS-RPT — це окремий стандарт (RFC 8460), не прив'язаний жорстко до MTA-STS. Він покриває і DANE, і STS одночасно. Суть: відправники, які підтримують TLS-RPT, щодоби надсилають вам агрегований JSON-звіт (gzip-вкладенням) про те, скільки сесій до вашого домену зашифрувалися успішно, а скільки — впали, і чому саме. Можна віддавати звіти й на HTTPS-ендпоінт: rua=https://tlsrpt.evilmail.pro/collect, але для старту mailto: простіший.
Читається просто: за добу Google зробив 1842 успішні TLS-сесії й 3 невдалі, усі три — через прострочений сертифікат, і несправний MX названо поіменно — mail.evilmail.pro. Ви бачите конкретний хост, який ламає шифрування, ще на фазі testing, коли пошта все одно доставляється.
Ключові result-type, за якими треба стежити:
starttls-not-supported — MX не запропонував STARTTLS (або хтось його вирізав дорогою — та сама downgrade-атака).
certificate-host-mismatch — CN/SAN не збігається з іменем MX.
validation-failure — ланцюжок довіри не зібрався.
sts-policy-fetch-error / sts-policy-invalid — відправник не зміг завантажити або розпарсити вашу політику (часто це і є редирект або битий Content-Type).
tlsa-invalid — для тих, хто паралельно використовує DANE.
Розкатка: none → testing → enforce без втрати пошти
Це головна операційна секція, і саме тут гинуть домени, які поспішили.
Фаза 1 — `mode: testing`, `max_age: 86400` (одна доба). У режимі testing відправники доставляють пошту рівно так, як і завжди — навіть якщо TLS падає. Але вони надсилають вам TLS-RPT про кожен збій. Тобто ви отримуєте повну картину реальних проблем шифрування, нічим не ризикуючи. Живіть у цьому режимі щонайменше 1–2 тижні. Читайте звіти щодня. Низький max_age тут навмисний: якщо ви щось наплутали в самій політиці, відправники швидко підхоплять виправлення.
Фаза 2 — перехід на `mode: enforce`. Перемикати можна тільки тоді, коли total-failure-session-count по всіх ваших MX дорівнює нулю кілька діб поспіль. Не «майже нуль», не «лише кілька дивних IP» — нуль. Кожен ненульовий збій у testing перетвориться на недоставлений лист у enforce.
Разом із переходом на enforce піднімайте max_ageпоетапно: 86400 → 604800 (тиждень) → 2592000 (місяць). Стеля за RFC — 31557600 (рік), але одразу ставити рік небезпечно. І на кожному кроці міняйте id.
Чому великий max_age на enforce — це пастка. Уявіть: ви поставили max_age: 31557600, а через місяць прогавили продовження сертифіката на приймаючому сервері. Тепер кожен відправник, який закешував вашу політику, цілий рік відмовлятиметься доставляти пошту, поки TLS не полагоджено — і ви не можете «відкликати» кеш. Kill-switch тут єдиний: поставити mode: none і змінити id, але дійде він лише до тих, у кого кеш уже протух. Тому дисципліна проста — тримайте max_age рівно таким великим, наскільки ви впевнені у своєму сертифікатному пайплайні.
Валідація й типові граблі
Перш ніж чіпати mode: enforce, проженіть весь ланцюжок вручну:
bash
# індикатор політики
dig +short TXT _mta-sts.evilmail.pro
# запис TLS-RPT
dig +short TXT _smtp._tls.evilmail.pro
# MX має збігатися з mx: у політиці
dig +short MX evilmail.pro
# сам файл політики — очікуємо HTTP/2 200 БЕЗ рядка Location:
curl -sv https://mta-sts.evilmail.pro/.well-known/mta-sts.txt
# ланцюжок і термін дії сертифіката приймаючого сервера
openssl s_client -connect mail.evilmail.pro:25 -starttls smtp -servername mail.evilmail.pro
У виводі curl не має бути жодного Location: і жодного 301/302 — тільки чистий 200 з Content-Type: text/plain. У виводі openssl дивіться на Verify return code: 0 (ok) і дату notAfter. Онлайн можна добити валідаторами: Hardenmail, MECSA, Google Admin Toolbox — вони покажуть проблему з того боку, з якого її бачить реальний відправник.
Реальні граблі, на які натикаються найчастіше:
Сертифікат на `mta-sts` протермінувався. Політика тихо перестає завантажуватись, у звітах — sts-policy-fetch-error, а на enforce пошта від великих відправників просто не йде. Автопродовження Let's Encrypt має покривати і цей піддомен.
MX у політиці не збігається з реальним MX-записом або з CN/SAN сертифіката приймаючого сервера.
Редирект на веб-фронті. http→https, www→apex, старий домен→новий — будь-який 3xx на .well-known/mta-sts.txt вбиває завантаження.
CRLF замість LF у файлі політики. Зберігайте у Unix-переносах.
Забули змінити `id` після правки політики — і зміни не доходять тижнями.
Чекліст перед enforce
[ ] mta-sts.evilmail.pro резолвиться (A/AAAA) і віддає файл по HTTPS.
[ ] Сертифікат на mta-sts валідний, довірений, CN/SAN = mta-sts.evilmail.pro, до закінчення далеко, автопродовження працює.
[ ] curl повертає 200, Content-Type: text/plain, безLocation: і без 3xx.
[ ] Поля mx: точно збігаються з dig MX і з CN/SAN сертифікатів приймаючих серверів.
[ ] Файл політики у LF-переносах, version: STSv1 першим рядком.
[ ] TXT _mta-sts опублікований, id у форматі timestamp і відповідає поточній версії файлу.
[ ] TXT _smtp._tls опублікований, поштова скринька tls-reports@ існує і хтось її читає.
[ ] Прожили щонайменше 1–2 тижні на mode: testing з max_age: 86400.
[ ] total-failure-session-count по всіх MX = 0 кілька діб поспіль.
[ ] max_age піднімаєте поетапно (86400 → 604800 → 2592000), а не одразу на рік; id міняєте на кожному переході.