У вас настроены SPF и DKIM. Письмо от вашего имени всё равно дойдёт
На разборе учебной рассылки мне показали письмо, отправленное от адреса компании ей же самой. Оно дошло во «Входящие». Без пометок, без папки «Спам», с правильным именем отправителя в списке писем.
Первое, что я подумал, - что-то не настроено. Полез в DNS: SPF на месте, ключ DKIM опубликован, подписи ставятся. Запись DMARC тоже нашлась. Всё, что положено, - есть.
Оказалось, что настроено всё правильно и работает как задумано. Просто задумано не то, что от этого ждут, - а в записи DMARC стояло значение, которое ничего не запрещает.
SPF проверяет не тот адрес, который вы видите
В письме два разных адреса отправителя, и это не ошибка, а устройство протокола.
Первый - конвертный, он же MAIL FROM, он же Return-Path. Его сообщает отправляющий сервер во время SMTP-сессии, и туда уходят уведомления о недоставке. Пользователь его не видит.
Второй - заголовок From внутри самого письма. Именно он показан в почтовом клиенте.
SPF - это список серверов, которым разрешено отправлять почту от домена. Проверяется по нему конвертный адрес. На заголовок From SPF не смотрит вообще.
Отсюда и весь фокус: отправитель ставит конвертный адрес на своём домене, где SPF, разумеется, проходит, а в заголовок From пишет ваш адрес. Проверка успешна и не про то.
Одна оговорка: при пустом конвертном адресе - так отправляются уведомления о недоставке - SPF проверяет имя, которым сервер представился в команде HELO.
DKIM устроен иначе: он подписывает заголовки и тело, и From в подпись обычно входит. Но DKIM отвечает только на вопрос «подпись верна», а не на вопрос «а тот ли домен подписал». Письмо, подписанное доменом атакующего и содержащее From с вашим доменом, - это письмо с корректной подписью.
То есть обе проверки отрабатывают штатно и обе оставляют заголовок From без внимания. Не потому, что недоработаны, - просто этот заголовок не входит в область их проверки.
Связывает их DMARC
Он добавляет к этому одно условие: домен, по которому прошла проверка, должен совпадать с доменом в заголовке From. Это совпадение называют выравниванием (alignment), и именно его в SPF и DKIM по отдельности нет.
Важная деталь, которую при чтении обычно пропускают: нужны не обе проверки сразу. Письмо проходит DMARC, если хотя бы одна из двух и прошла, и оказалась выровнена с доменом из From.
И второе, что делает DMARC: говорит принимающей стороне, что делать с письмом, которое условие не выполнило. Запись лежит в DNS и читается одной командой:
$ dig +short TXT _dmarc.github.com
"v=DMARC1; p=quarantine; sp=reject; pct=100; rua=mailto:dmarc@github.com; ruf=mailto:dmarc@github.com; fo=1"
p= - политика для самого домена: none, quarantine или reject. sp= - отдельная политика для поддоменов. rua= - адрес для сводных отчётов. ruf= - адрес для подробных отчётов по отдельным письмам; в них попадают фрагменты чужой переписки, поэтому включать его стоит осознанно.
Отдельно про pct=. Это доля писем, к которым применяется политика. pct=100 означает «ко всем». А вот pct=20 при p=reject означает не то, что кажется: к четырём письмам из пяти применится не reject, а следующая по мягкости политика, то есть quarantine. Отвергнуты они не будут. Значение по умолчанию - 100, но его часто занижают на время внедрения и забывают вернуть.
И ещё два параметра - adkim и aspf - задают строгость выравнивания. По умолчанию r (relaxed) - сверяются организационные домены, то есть подпись example.com выравнивается и с адресом mail.example.com в From. Значение s (strict) требует точного совпадения.
p=none - это не защита
Вот здесь и была причина. У домена стояло p=none.
p=none означает буквально: «проверяйте, присылайте мне отчёты, но письмо доставляйте как обычно». Никакой фильтрации. Запись есть, отчёты идут, панель мониторинга рисует зелёное - а письмо от вашего имени доходит во «Входящие».
Режим этот придуман не зря: включать reject вслепую опасно. У любой компании почта уходит не только с основного почтового сервера - рассылки, счета из бухгалтерской программы, уведомления от систем мониторинга, письма от сервиса поддержки. Часть из них шлётся так, что выравнивания нет, и переход на reject их убивает. Поэтому p=none - это стартовая позиция, из которой смотрят отчёты и чинят своих отправителей.
Проблема в том, что на стартовой позиции остаются годами. Пункт «настроить DMARC» в чек-листе закрыт, а защиты нет.
Поддомены проверяются не тем параметром, о котором думают
Тонкость, на которой ловятся и те, кто дошёл до reject. Политика поддоменов задаётся параметром sp=. Если его нет, поддомены наследуют p=. А если он есть и в нём none, то все поддомены, у которых нет собственной записи _dmarc, остаются без защиты:
v=DMARC1; p=reject; sp=none;
Для атакующего разницы почти никакой: адрес noreply@mail.вашдомен выглядит для получателя ровно так же убедительно, как noreply@вашдомен. Проверять sp= надо отдельно, потому что своей записи у поддомена обычно нет и в выводе dig для него будет пусто - а пусто здесь означает «действует правило родителя», а не «ничего не настроено».
Домен, с которого не шлют писем
Отдельный случай - домены, которые вообще не отправляют почту: припаркованные, купленные на будущее, оставшиеся от старого бренда. Их регулярно забывают, и они удобны для подделки именно потому, что настоящей почты с них нет и жалоб не будет.
Для них есть готовая конфигурация, и она видна на example.com:
$ dig +short TXT example.com | grep spf
"v=spf1 -all"
$ dig +short TXT _dmarc.example.com
"v=DMARC1;p=reject;sp=reject;adkim=s;aspf=s"
$ dig +short TXT '*._domainkey.example.com'
"v=DKIM1; p="
$ dig +short MX example.com
0 .
v=spf1 -all - отправлять не может никто. p=reject вместе с sp=reject и строгим выравниванием - отвергать и за домен, и за поддомены.
Запись DKIM здесь стоит на маске *._domainkey, и это принципиально: пустой p= отзывает ключ только того селектора, для которого запись опубликована. Поставив её на selector1, вы закроете подделку ровно под selector1 и оставите открытыми все остальные имена селекторов.
Последняя строка - нулевая MX-запись: одна точка вместо имени сервера означает «этот домен почту не принимает». Четыре записи. Ставятся один раз, обслуживания не требуют.
Что этим не закрывается
DMARC защищает ваш домен в заголовке From. Он ничего не может сделать с похожим доменом: вашдомен-support.com, вашдомен.co, а также с именем, в котором одна из букв заменена на визуально неотличимую из другого алфавита - на странице такой подмены не видно вовсе, в этом и смысл приёма. У таких доменов свой DMARC и свои валидные подписи, и с точки зрения протокола всё в порядке.
Он не влияет на отображаемое имя. Строка From: "Иван Петров, финансовый директор" <noreply@чужойдомен.com> - валидна, и во многих клиентах на телефоне видно только «Иван Петров, финансовый директор».
Он не помогает, если письмо пришло с настоящего взломанного ящика внутри вашей же компании.
И он ничего не решает во внутренней пересылке: письмо, попавшее внутрь и пересланное через список рассылки, обычно теряет выравнивание, поэтому у списков рассылки есть свои механизмы, и с ними придётся разбираться отдельно.
Порядок внедрения
Опубликовать запись с p=none и адресом для отчётов. Отчёты приходят в XML, читать их глазами тяжело - нужен любой разборщик, свой или готовый.
Смотреть отчёты и находить в них своих отправителей, которые не проходят выравнивание. Агрегированные отчёты приходят раз в сутки, и на сбор картины уходит от пары недель в небольшой компании до квартала в тех, где есть сезонные рассылки. Их всегда больше, чем ожидаешь: почти в каждой компании есть система, про которую никто не помнил, что она шлёт письма.
Починить их. Достаточно, чтобы у каждого отправителя работал хотя бы один выровненный механизм: либо он попадает в ваш SPF, либо подписывает письма ключом вашего домена. Только после этого двигать политику. Переходить сначала на quarantine, а не сразу на reject.
И отдельно, сразу и без ожидания, - закрыть теми же четырьмя записями все домены, с которых почта не отправляется. Там ждать нечего: ломать нечего.
Что посмотреть у себя
Прочитать свою запись _dmarc и посмотреть на значение p=. Если none - политика не применяется, и это главный вывод статьи.
Посмотреть, есть ли sp=. Нет - поддомены наследуют p=; есть со значением none - поддомены открыты.
Перебрать домены, которые числятся за компанией, и проверить каждый, а не только основной. Забытые домены как раз и остаются без записей.
Проверить, приходят ли отчёты по адресу из rua=. Ящик, который никто не открывал год, - обычное дело, и тогда p=none не даёт даже той пользы, ради которой его ставили.
KioskNews shows a cleaned-up reading view extracted from the publisher’s page — the original always lives on their site, not ours.