MTA-STS: как защитить email от downgrade-атак
SMTP was designed in 1982. Encryption was bolted on later through STARTTLS - an opportunistic upgrade that any man-in-the-middle can strip away in transit. MTA-STS fixes this by telling the world: “My mail server requires TLS. If you can’t establish an encrypted connection, don’t deliver the message at all.” This article walks through the protocol, its DNS and HTTPS components, and a complete setup from scratch.
The problem: STARTTLS is optional by default
When a sending mail server connects to a receiving server, it opens a plaintext SMTP session on port 25 and asks whether the receiver supports STARTTLS. If the answer is yes, both sides negotiate a TLS handshake and continue over an encrypted channel. If the answer is no - or if someone on the network removes the STARTTLS response before it reaches the sender - the message travels in plaintext.
This is a downgrade attack. The attacker does not need to break TLS. They only need to intercept one line of text in the SMTP handshake. On networks where BGP hijacking or DNS spoofing is possible, stripping STARTTLS is straightforward.
DANE (DNS-Based Authentication of Named Entities) solved part of this by publishing TLSA records in DNSSEC-signed zones. But DANE requires DNSSEC, and DNSSEC adoption remains low. According to APNIC data from late 2025, fewer than 5% of .com domains are DNSSEC-signed. MTA-STS takes a different approach: it uses HTTPS - infrastructure that already exists everywhere - to distribute the TLS policy.
How MTA-STS works
MTA-STS (RFC 8461) has two components: a DNS TXT record that signals the policy exists, and an HTTPS-hosted policy file that contains the actual rules. The sending server checks both before delivering mail.
The flow, step by step:
- Sender resolves MX records for the recipient domain.
- Sender queries
_mta-sts.example.comfor a TXT record. - If the record exists, sender fetches
https://mta-sts.example.com/.well-known/mta-sts.txt. - Sender caches the policy for the duration specified in
max_age. - On every subsequent delivery, sender enforces the policy: require valid TLS, verify the MX hostname against the
mxpatterns in the policy. If TLS negotiation fails, the message is not delivered.
The HTTPS part is critical. An attacker who can tamper with DNS can spoof a TXT record, but forging a valid TLS certificate for mta-sts.example.com is a different problem entirely. This is where the security comes from.
Step 1. Create the policy file
The policy file is a plain text file served at a fixed URL. It has exactly four fields.
version: STSv1 mode: enforce mx: mail.example.com mx: *.example.com max_age: 604800
Field-by-field breakdown:
version: STSv1- the only valid version right now.mode- three options:enforce,testing,none. Start withtestingto collect reports without blocking mail. Move toenforceonce you are confident.mx- list of MX hostnames that the policy allows. One line per hostname. Wildcards work:*.example.commatchesmail.example.com. These must match the Common Name or SAN in the server’s TLS certificate.max_age- cache lifetime in seconds. 604800 = one week. RFC recommends values between one day (86400) and several weeks. Do not set this too low - if your HTTPS endpoint goes down, a short max_age means senders will stop enforcing TLS quickly.
For Google Workspace domains, the MX hostnames are Google’s servers:
version: STSv1 mode: enforce mx: aspmx.l.google.com mx: *.aspmx.l.google.com mx: *.googlemail.com max_age: 604800
For Microsoft 365, the pattern is different:
version: STSv1 mode: enforce mx: *.mail.protection.outlook.com max_age: 604800
Step 2. Serve the policy over HTTPS
The file must be accessible at exactly https://mta-sts.example.com/.well-known/mta-sts.txt. This means you need a subdomain mta-sts pointed at a web server with a valid TLS certificate. A self-signed certificate will not work - senders validate the chain.
Nginx configuration:
server {
listen 443 ssl;
server_name mta-sts.example.com;
ssl_certificate /etc/letsencrypt/live/mta-sts.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mta-sts.example.com/privkey.pem;
location = /.well-known/mta-sts.txt {
root /var/www/mta-sts;
default_type text/plain;
}
location / {
return 404;
}
}Place the policy file at /var/www/mta-sts/.well-known/mta-sts.txt. Get a certificate with Let’s Encrypt:
certbot certonly --nginx -d mta-sts.example.com
If you do not want to maintain a separate server, host the file on a static hosting platform. GitHub Pages, Cloudflare Pages, or S3 + CloudFront all work. The only requirement: valid HTTPS on the mta-sts subdomain.
Step 3. Add the DNS TXT record
Create a TXT record at _mta-sts.example.com:
_mta-sts.example.com. IN TXT "v=STSv1; id=20260427001"
The id field is a string that senders use to detect policy changes. Every time you update the policy file, change the id. A timestamp-based format (like 20260427001) works well: date plus a sequence number. Senders compare the id in DNS against the cached policy. If they differ, the sender re-fetches the policy file.
Step 4. Enable TLS-RPT reporting
TLS-RPT (RFC 8460) is the reporting mechanism for MTA-STS. It tells senders where to send reports about TLS negotiation failures. Without it, you are flying blind - you will not know if legitimate mail is being blocked due to a TLS misconfiguration.
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"
Reports arrive as JSON files (gzipped) attached to emails. They contain information about successful and failed TLS connections: the sending IP, the result type (certificate-expired, starttls-not-supported, validation-failure), and the count.
A sample TLS-RPT report (simplified):
{
"organization-name": "Google Inc.",
"date-range": {
"start-datetime": "2026-04-27T00:00:00Z",
"end-datetime": "2026-04-28T00:00:00Z"
},
"policies": [
{
"policy": {
"policy-type": "sts",
"policy-string": ["version: STSv1", "mode: enforce", "mx: mail.example.com", "max_age: 604800"],
"policy-domain": "example.com"
},
"summary": {
"total-successful-session-count": 1542,
"total-failure-session-count": 0
}
}
]
}When total-failure-session-count is zero, your setup is clean. Any non-zero value warrants investigation. The failure-details array (omitted above for brevity) will specify the exact failure type and the receiving MX hostname where it occurred.
Verification: full check sequence
# 1. Check the DNS TXT record dig TXT _mta-sts.example.com +short # 2. Fetch the policy file curl -sS https://mta-sts.example.com/.well-known/mta-sts.txt # 3. Verify the TLS certificate on the MX host openssl s_client -connect mail.example.com:25 -starttls smtp -servername mail.example.com < /dev/null 2>/dev/null | openssl x509 -noout -subject -dates # 4. Check the TLS-RPT record dig TXT _smtp._tls.example.com +short
All four checks should return results. The openssl step is important: it confirms that the MX server presents a valid TLS certificate with a hostname matching the mx entries in your policy. A mismatch here means senders in enforce mode will refuse to deliver.
Проблема: STARTTLS не гарантирует шифрование
SMTP изначально передаёт почту открытым текстом. STARTTLS добавляет шифрование, но оппортунистически: если принимающий сервер объявляет поддержку - соединение шифруется, если нет - письмо уходит без шифрования. Атакующий в позиции man-in-the-middle может вырезать строку 250 STARTTLS из ответа сервера. Отправляющая сторона решит, что TLS не поддерживается, и передаст содержимое в открытом виде. Это downgrade-атака.
DANE решает эту задачу через DNSSEC и TLSA-записи. Но DNSSEC подписано менее 5% доменов в зоне .com (данные APNIC на конец 2025). MTA-STS предлагает другой путь: политика TLS публикуется через HTTPS, а подлинность гарантируется сертификатом веб-сервера. Инфраструктура HTTPS есть у всех - отдельная подпись зоны DNS не нужна.
Как работает MTA-STS
MTA-STS описан в RFC 8461. Протокол состоит из двух частей: DNS-запись _mta-sts сигнализирует о наличии политики, а файл на https://mta-sts.домен/.well-known/mta-sts.txt содержит саму политику.
Последовательность при доставке:
- Отправитель резолвит MX-записи домена получателя.
- Запрашивает TXT-запись
_mta-sts.example.com. - Если запись найдена - загружает файл политики по HTTPS.
- Кеширует политику на время
max_age. - При каждой последующей доставке проверяет: TLS-соединение установлено, сертификат валиден, имя хоста MX совпадает с паттернами в политике. Если проверка не пройдена и режим
enforce- письмо не доставляется.
Безопасность строится на том, что подделать TLS-сертификат для поддомена mta-sts.example.com несопоставимо сложнее, чем подменить DNS-ответ.
Шаг 1. Файл политики
Текстовый файл с четырьмя полями. Размещается по фиксированному пути:
version: STSv1 mode: testing mx: mail.example.com mx: backup-mx.example.com max_age: 86400
Режимы:
testing- отправители доставляют письмо даже при ошибке TLS, но отправляют TLS-RPT отчёт. Используйте на старте.enforce- строгий режим. Нет TLS - нет доставки. Переходите сюда после анализа отчётов.none- отключение политики. Используется при выводе из эксплуатации.
Значение max_age определяет время кеширования. 86400 секунд (сутки) - разумное начальное значение. После перехода в enforce увеличьте до 604800 (неделя) или выше. Чем дольше кеш, тем дольше отправители будут требовать TLS, даже если ваш HTTPS-сервер с политикой временно упадёт.
Шаг 2. Веб-сервер для раздачи политики
Нужен поддомен mta-sts.example.com с валидным TLS-сертификатом. Самоподписанные сертификаты не подходят - отправляющие серверы проверяют цепочку доверия.
Конфигурация Nginx:
server {
listen 443 ssl;
server_name mta-sts.example.com;
ssl_certificate /etc/letsencrypt/live/mta-sts.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/mta-sts.example.com/privkey.pem;
location = /.well-known/mta-sts.txt {
root /var/www/mta-sts;
default_type text/plain;
}
location / {
return 404;
}
}Альтернатива для тех, кто не хочет держать отдельный сервер: разместить файл на GitHub Pages или Cloudflare Pages. Создайте репозиторий, положите файл в .well-known/mta-sts.txt, привяжите кастомный домен mta-sts.example.com. HTTPS обеспечит платформа.
Шаг 3. DNS-записи
Две записи: A/CNAME для поддомена и TXT для сигнала.
# A-запись для веб-сервера с политикой mta-sts.example.com. IN A 203.0.113.10 # TXT-запись — сигнал отправителям _mta-sts.example.com. IN TXT "v=STSv1; id=20260427001"
Поле id меняется при каждом обновлении политики. Отправитель сравнивает id из DNS с кешированным значением. Если не совпадает - перезагружает файл политики. Формат произвольный; дата с порядковым номером - удобная практика.
Шаг 4. TLS-RPT: отчёты о проблемах
Без TLS-RPT (RFC 8460) вы не узнаете, блокируется ли легитимная почта из-за ошибок TLS. Добавьте запись:
_smtp._tls.example.com. IN TXT "v=TLSRPTv1; rua=mailto:tls-reports@example.com"
Google, Microsoft, Yahoo и другие крупные отправители поддерживают TLS-RPT и будут присылать ежедневные отчёты в формате JSON. Отчёт содержит количество успешных и неуспешных TLS-сессий, типы ошибок и IP-адреса отправителей.
Отдельный ящик для отчётов - хорошая практика. Объём невелик (десятки писем в день для домена со средним трафиком), но смешивать с рабочей почтой неудобно. Для автоматического парсинга можно использовать tlsrpt-processor или написать скрипт, который распаковывает .gz и собирает метрики.
Скрипт проверки MTA-STS
Bash-скрипт, который проверяет все компоненты MTA-STS за один запуск:
#!/bin/bash
DOMAIN=${1:?"Usage: $0 domain.com"}
echo "=== MTA-STS check for $DOMAIN ==="
echo ""
# DNS TXT record
echo "[1] _mta-sts.$DOMAIN TXT record:"
dig TXT "_mta-sts.$DOMAIN" +short
echo ""
# Policy file
POLICY_URL="https://mta-sts.$DOMAIN/.well-known/mta-sts.txt"
echo "[2] Policy file ($POLICY_URL):"
HTTP_CODE=$(curl -sS -o /tmp/mta-sts-policy.txt -w "%{http_code}" "$POLICY_URL")
if [ "$HTTP_CODE" = "200" ]; then
cat /tmp/mta-sts-policy.txt
else
echo "FAIL: HTTP $HTTP_CODE"
fi
echo ""
# TLS on MX hosts
echo "[3] MX records and TLS check:"
MX_HOSTS=$(dig MX "$DOMAIN" +short | awk '{print $2}' | sed 's/\.$//')
for MX in $MX_HOSTS; do
CERT_CN=$(openssl s_client -connect "$MX:25" -starttls smtp \
-servername "$MX" < /dev/null 2>/dev/null | \
openssl x509 -noout -subject 2>/dev/null)
if [ -n "$CERT_CN" ]; then
echo " $MX — TLS OK ($CERT_CN)"
else
echo " $MX — TLS FAIL"
fi
done
echo ""
# TLS-RPT
echo "[4] _smtp._tls.$DOMAIN TXT record:"
dig TXT "_smtp._tls.$DOMAIN" +shortЗапуск:
chmod +x check-mta-sts.sh ./check-mta-sts.sh example.com
Типичные ошибки
- MX-хост не совпадает с паттерном в политике. MX-запись указывает на
mx1.mailprovider.com, а в политике прописан*.example.com. Сертификат MX-хоста тоже не содержитexample.com. Результат: в режиме enforce почта не доставляется. Решение: в полеmxукажите хостнеймы из MX-записей, а не свой домен. - Просроченный сертификат на mta-sts поддомене. Let’s Encrypt-сертификат не продлился автоматически. Отправители не могут загрузить политику. Пока кеш не истёк, используется старая политика. После истечения
max_age- защита пропадает. Настройте мониторинг сертификата. - Забыли обновить id в DNS. Изменили файл политики, но не изменили
idв TXT-записи. Отправители не знают, что политика обновилась, и продолжают использовать кеш. - Режим enforce без тестирования. Включили enforce сразу, не проанализировав TLS-RPT отчёты. Выяснилось, что один из MX-серверов возвращает сертификат без нужного SAN. Часть почты заблокирована. Всегда начинайте с
mode: testing. - CNAME вместо A-записи для mta-sts поддомена, указывающий на домен без HTTPS. CNAME на
example.github.io- допустимо. CNAME на IP-адрес - нет (CNAME не может указывать на IP). Убедитесь, что целевой хост отдаёт сертификат дляmta-sts.example.com.
MTA-STS на стороне отправителя: Postfix
MTA-STS защищает входящую почту. Но если вы управляете собственным почтовым сервером, стоит также включить проверку MTA-STS при отправке. Postfix не поддерживает MTA-STS из коробки, но есть плагин postfix-mta-sts-resolver (пакет в PyPI).
# Установка
pip install postfix-mta-sts-resolver
# Конфигурация /etc/mta-sts-daemon.yml
host: 127.0.0.1
port: 8461
cache:
type: sqlite
options:
filename: /var/cache/mta-sts/cache.db
# Добавить в /etc/postfix/main.cf
smtp_tls_policy_maps = socketmap:inet:127.0.0.1:8461:postfix
# Перезапуск
systemctl restart mta-sts-daemon
systemctl reload postfixПосле этого Postfix будет запрашивать MTA-STS-политику для каждого домена-получателя и применять её при доставке. Если принимающий домен опубликовал mode: enforce, Postfix откажется отправлять без TLS.
MTA-STS в связке с SPF, DKIM, DMARC
MTA-STS решает отдельную задачу - защиту транспортного канала. SPF, DKIM и DMARC защищают содержимое: подтверждают отправителя и целостность письма. Это разные уровни, и они не заменяют друг друга.
Полный стек email-безопасности выглядит так: SPF + DKIM + DMARC (аутентификация) + MTA-STS (шифрование транспорта) + BIMI (визуальная идентификация). Каждый слой закрывает свой вектор атаки. MTA-STS - единственный из них, который защищает от прослушивания трафика между серверами.
Подробнее о настройке аутентификации - пошаговая инструкция по SPF, DKIM и DMARC. О визуальном слое - руководство по BIMI.
Кому и когда внедрять MTA-STS
Если ваш домен принимает почту - MTA-STS полезен. Он защищает входящие письма от перехвата. Настройка занимает 20-40 минут: файл, DNS-запись, веб-сервер. Поддерживать почти нечего - разве что продление сертификата, которое автоматизируется через certbot.
Для компаний в финансовом секторе, здравоохранении и других регулируемых отраслях MTA-STS - не опция, а требование. PCI DSS 4.0 прямо указывает на необходимость шифрования email-трафика при передаче конфиденциальных данных.
Google Workspace и Microsoft 365 уже публикуют MTA-STS для клиентских доменов (при включении в админ-панели). Если вы используете одну из этих платформ, проверьте настройки - возможно, осталось только создать DNS-записи.
Безопасность канала - половина дела
MTA-STS гарантирует, что письма не перехватят по дороге. Но если в вашей базе рассылки мёртвые адреса, спам-ловушки и несуществующие домены, доставляемость упадёт задолго до того, как кто-то попробует downgrade-атаку. Чистая база - фундамент, на котором держится всё остальное: аутентификация, шифрование, репутация.
Прежде чем настраивать MTA-STS, убедитесь, что база в порядке. Загрузите список в uChecker - увидите невалидные адреса, рискованные контакты и потенциальные ловушки за минуты.
