На разборе учебной рассылки мне показали письмо, отправленное от адреса компании ей же самой. Оно дошло во «Входящие». Без пометок, без папки «Спам», с правильным именем отправителя в списке писем.

Первое, что я подумал, - что-то не настроено. Полез в 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 не даёт даже той пользы, ради которой его ставили.

Комментарии (0)