DANE и записи TLSA для SMTP: привязка сертификата через DNSSEC против downgrade-атак
STARTTLS в почте оппортунистичен: MITM вырезает одну строку из ответа сервера — и письмо с токеном сброса пароля уходит открытым текстом. Разбираем, почему MTA-STS с его TOFU-окном лечит проблему лишь наполовину, как DANE переносит корень доверия в DNSSEC и жёстко привязывает сертификат MX, чем 3 1 1 отличается от 2 1 1 и как катить ротацию ключа без шторма NDR.
EvilMail Team18 июля 2026 г.12 мин чтения
Как выглядит downgrade-атака на SMTP на самом деле
STARTTLS в SMTP оппортунистичен по конструкции, и это не философский изъян, а эксплуатируемая дыра на проводе. Отправляющий MTA соединяется на порт 25, шлёт EHLO, принимающий сервер перечисляет возможности, среди которых строка 250-STARTTLS. Сессия ещё не зашифрована. Любой, кто сидит на пути между двумя MTA — провайдерский middlebox, скомпрометированный роутер, угнанный через BGP-hijack префикс — просто вырезает эту строку из ответа или подменяет её на 250-XXXXXXXX. Отправитель не видит предложения шифроваться и спокойно продолжает в открытом виде. Никакой ошибки, никакого алерта.
Дальше письмо с токеном сброса пароля, счётом-фактурой или внутренней перепиской уходит plaintext, и его читают в реальном времени. PKIX — публичные доверенные CA — тут вообще ни при чём: сертификат никто не предъявлял, потому что до TLS дело не дошло. Без внешней политики отправитель не знает, что шифрование вообще ожидалось: отсутствие TLS для него неотличимо от нормы.
Отсюда две конкурирующие политики «TLS обязателен»: MTA-STS и DANE. Обе заставляют отправителя заранее знать, что этот получатель обязан говорить по TLS с валидным сертификатом. Но модели доверия у них разные, и эта разница определяет, кого из атакующих они реально ловят. MTA-STS — заплатка поверх сломанной модели доверия; DANE переносит корень доверия в DNSSEC. Для серьёзной инфраструктуры в 2026-м правильный ответ — не «или», а оба, с ясным пониманием, кто закрывает какой класс атак.
DANE и TLSA для SMTP: защита от STARTTLS-downgrade через DNSSEC — EvilMail Blog
MTA-STS: доверие через HTTPS и его слепые зоны
MTA-STS устроен так. Домен публикует политику по адресу https://mta-sts.<domain>/.well-known/mta-sts.txt и кладёт TXT-запись _mta-sts.<domain> с полем id, которое меняется при каждом обновлении политики. Отправитель видит id, тянет файл политики по HTTPS, читает список разрешённых MX, режим (testing или enforce) и max_age — как долго кэшировать. В режиме enforce при отсутствии валидного TLS к перечисленному MX доставка откладывается.
Проблема — в корне доверия. Он состоит из публичного PKIX (валидный сертификат на веб-сервере mta-sts.<domain>) и доступности этого веб-сервера. А сама TXT-запись _mta-sts ничем не подписана. Если у домена нет DNSSEC, атакующий, контролирующий сеть, при первой доставке или после истечения кэша отдаёт отправителю поддельный id, уводит его на свой эндпоинт и скармливает свою политику. Это классический TOFU — trust on first use: гарантия появляется только после того, как честная политика уже осела в кэше, а окно первой доставки и окно протухшего кэша остаются уязвимыми для активного атакующего.
Плюс операционная зависимость: если HTTPS-эндпоинт с политикой упал, отправители работают по последнему кэшу, а по его истечении деградируют к оппортунистическому TLS. MTA-STS хорош тем, что не требует DNSSEC и катится легко. Но его модель доверия принципиально мягче, и против MITM, который сидит на канале с первого пакета, он даёт лишь вероятностную защиту.
DANE: привязка сертификата в DNSSEC-подписанной зоне
DANE (RFC 7672 для SMTP) работает иначе. Под именем MX-хоста публикуется запись TLSA:
_25._tcp.mx1.example.com. 3600 IN TLSA 3 1 1 <hex-хэш SPKI>
Отправляющий MTA, резолвя MX и затем TLSA, проверяет, что ответ пришёл с установленным AD-битом — то есть DNSSEC валиден до корня. Наличие валидной TLSA само по себе — сигнал «TLS обязателен, и вот отпечаток сертификата, который я жду». Downgrade становится бесполезен: даже если MITM вырежет 250-STARTTLS, отправитель уже знает из DNS, что шифрование обязательно, и отклонит plaintext-доставку. Никакого TOFU-окна нет — отказ строгий с самой первой доставки.
Формат TLSA — три числовых поля плюс данные:
Usage: 0=PKIX-TA, 1=PKIX-EE, 2=DANE-TA, 3=DANE-EE. Для SMTP используют только 2 и 3. Варианты 0/1 требуют валидной публичной PKIX-цепочки, что для MTA нецелесообразно и запрещено RFC 7672.
Selector: 0 = весь сертификат, 1 = SubjectPublicKeyInfo (SPKI, только публичный ключ).
Критично: без DNSSEC DANE не существует. TLSA в неподписанной зоне отправители обязаны игнорировать — иначе тот же MITM подделал бы и саму TLSA. Именно DNSSEC-подпись превращает опциональный TLS в неотзываемое требование.
3 1 1 против 2 1 1: что это меняет при ротации
Практически вся боль DANE — вокруг того, к чему именно привязан пин.
`3 1 1` (DANE-EE + SPKI + SHA-256) привязывается к публичному ключу листового сертификата. Он не зависит от CA вообще — неважно, какой промежуточный подписал лист, важен только SPKI. Минус: при каждой смене ключа надо обновлять TLSA. Но если продлевать сертификат с тем же ключом (--reuse-key в certbot), TLSA переживает продление без единого касания.
`2 1 1` (DANE-TA) привязывается к промежуточному или корневому CA. Он переживает ротацию листа при том же CA — но ломается, как только CA меняет промежуточный сертификат. А автоматические ACME-цепочки меняют промежуточные без анонса: Let's Encrypt делал это не раз, и каждый такой случай ловил врасплох всех, кто пинил 2 1 1. Внезапно у вас глобальный отказ на входящую почту, хотя свой сервер вы не трогали.
Для типовой инфраструктуры на Let's Encrypt/ACME рекомендация однозначна: `3 1 1` плюс автоматическая публикация TLSA из renewal-хука. Вы контролируете свой ключ и не зависите от политики CA по промежуточным. Во время ротации можно и нужно держать несколько TLSA-записей одновременно — семантика матчинга у них OR: сертификат проходит, если совпал хотя бы с одной.
Ротация ключа без шторма NDR
Самая частая причина сбоя DANE — рассинхрон между сертификатом на сервере и TLSA в DNS. Если на сервере уже новый ключ, а в DNS ещё только хэш старого, отправители перестают матчить сертификат и откладывают всю входящую почту. Через несколько дней это превращается в шквал NDR, и о проблеме вы узнаёте от клиентов, а не от мониторинга. Порядок операций здесь не «best practice», а единственно верный.
Правильная последовательность для 3 1 1:
1.Сгенерировать новый ключ/CSR заранее, ещё до выпуска сертификата.
2.Опубликовать вторую TLSA-запись с хэшем нового SPKI и дождаться распространения: max(TTL записи, время ресайна зоны DNSSEC) плюс запас на кэши нижестоящих резолверов.
3.Только теперь переключить сертификат на сервере.
4.После того как весь входящий трафик пошёл на новый сертификат и старый TTL полностью истёк — удалить старую TLSA.
Между шагами 2 и 4 в зоне живут обе записи, и благодаря OR-семантике проходит и старый, и новый сертификат — это и есть окно без риска. Хэш нового ключа можно посчитать до выпуска сертификата прямо из приватного:
Про DNSSEC-специфику: учитывайте не только TTL записи, но и период ресайна зоны. Если подпись обновляется по расписанию раз в час, ваше «дождаться» должно это покрывать. TTL для TLSA держите умеренным, порядка 3600, чтобы окно ротации не растягивалось на сутки, но помните, что агрессивно кэширующие резолверы всё равно могут держать старое значение чуть дольше.
Практика: команды, записи, верификация
Посчитать 3 1 1-хэш из уже выпущенного сертификата:
_25._tcp.mx1.example.com. 3600 IN TLSA 3 1 1 \
8f2a9c4e...d1b0
Проверка на проводе — из пакета postfix:
bash
posttls-finger -c -l dane mx1.example.com
# ищем: Verified TLS connection ... Matched TLSA record
Проверить, что зона реально подписана и пришёл AD-бит:
bash
delv @1.1.1.1 _25._tcp.mx1.example.com TLSA
# или
dig +dnssec _25._tcp.mx1.example.com TLSA # флаг ad в заголовке
Postfix-сторона для исходящей почты (обязательная валидация DANE):
# main.cf
smtp_dns_support_level = dnssec
smtp_tls_security_level = dane
Уровень dane оппортунистичен-но-строг: при наличии валидной TLSA у получателя TLS становится обязательным и проверяется, при отсутствии — деградирует к обычному оппортунистическому. Для приёма в Postfix настраивать ничего не нужно — нужна лишь корректная TLSA под каждым MX-хостом и подписанная DNSSEC-зона; всю работу делает отправитель. Из внешних валидаторов — internet.nl, dane.sys4.de/smtp, Hardenize.
DANE или MTA-STS? Ставьте оба
DANE даёт строгую защиту без TOFU-окна против активного сетевого атакующего с первой же доставки, но требует DNSSEC и дисциплины ротации, а при ошибке ломается тихо и глобально. MTA-STS не требует DNSSEC, катится проще, деградирует мягче, но в TOFU-окне слабее против MITM на канале.
Они не взаимоисключающие, и крупные отправители проверяют обе политики. Если ваша зона уже под DNSSEC — DANE обязателен, а MTA-STS enforce ставится вторым слоем ради отправителей, которые до сих пор не валидируют DNSSEC (а таких всё ещё заметная доля). Если DNSSEC нет и не планируется — минимум MTA-STS в режиме enforce, но это осознанный компромисс, а не эквивалент.
Чек-лист перед включением enforce
DNSSEC подписан и валидируется до корня — проверено через delv, AD-бит на месте.
TLSA 3 1 1 опубликована для каждого MX-хоста (_25._tcp.mx…), а не на apex домена — это классическая фатальная ошибка.
TTL TLSA разумный (~3600) с учётом периода ресайна зоны.
Renewal-хук (certbot --deploy-hook) публикует новую TLSA автоматически.
Вторая TLSA публикуется до смены сертификата на сервере.
Мониторинг матча TLSA↔живой сертификат с алертом при расхождении.
Проверка через internet.nl и posttls-finger -l dane даёт Verified.
Postfix исходящий: smtp_tls_security_level = dane + smtp_dns_support_level = dnssec.
MTA-STS enforce поднят как второй слой для отправителей без DNSSEC-валидации.
Один рассинхрон TLSA кладёт весь входящий поток тихо и надолго. Автоматизируйте публикацию из хука, держите мониторинг на расхождении пина — и DANE из источника ночных NDR превращается в то, чем должен быть: неотзываемым «нет» любому downgrade.