RE:NODE

Веб-хостинг13 мин чтения

SPF, DKIM и DMARC: почему ваша почта попадает в спам

Три DNS-записи, доказывающие, что письмо действительно ваше, как их связывает alignment и в каком порядке вводить их, не сломав отправку.

0 прочтений

Почта с вашего домена попадает в спам, потому что принимающий сервер не может доказать, что она пришла от вас. Это исправляют три DNS-записи: SPF перечисляет серверы, которым разрешено отправлять почту от имени вашего домена, DKIM прикладывает криптографическую подпись, которую получатель проверяет по открытому ключу в вашей зоне, а DMARC сообщает получателю, что делать, когда ни то ни другое не согласуется с адресом, который на самом деле видит читатель. Все три - записи TXT. Ни одна не сложна. Сложна идея, которая лежит посередине, - alignment, - и почти любая история «я настроил SPF, а всё равно не проходит» - это именно то, что её упустили.

Сразу одно, чтобы не мешало, потому что от этого зависит, что вам стоит читать: RE:NODE не хостит почту. Ни почтовых ящиков, ни MX, ни релея SMTP, ни зон DNS тоже нет: записи ниже создаются там, куда указывают неймсерверы вашего домена, обычно у регистратора. Мы хостим приложение или сайт, который отправляет письма, и этому посвящён конец статьи.

Два адреса From и почему в этом вся проблема#

У SMTP-сообщения два отправителя, и они не обязаны совпадать.

Отправитель конверта - это то, что отправляющий сервер объявляет командой MAIL FROM в ходе SMTP-диалога. Именно туда уходят возвраты, он записывается в заголовок Return-Path, и в почтовом клиенте его никто никогда не видит. У почтовой формы на сайте это часто что-то вроде bounces@sendgrid.net или www-data@server42.hostingco.net.

Заголовок From - это строка внутри самого письма вида From: Anna <anna@example.com>. Только её читает человек, и именно её хочет подделать фишер.

SPF проверяет отправителя конверта. DKIM подписывает сообщение и называет домен подписи. Ни одна из проверок сама по себе ничего не говорит об адресе в строке From:, а ведь именно им злоупотребляют. DMARC закрывает этот разрыв: он требует, чтобы одна из успешных проверок относилась к тому же домену, что и видимый From:, и позволяет вам опубликовать, что делать, когда этого нет ни у одной.

SMTP на 25согласуется с From?согласуется с From?решает p=Ваш отправительприложение, релей, ящикПринимающий серверGmail, OutlookПроверка SPFдомен конвертаПроверка DKIMоткрытый ключ селектораПолитика DMARCTXT-запись _dmarcInbox, спам или отказ
Что проверяет принимающий сервер перед доставкой

SPF: каким серверам разрешено отправлять#

SPF - это одна запись TXT на apex вашего домена, перечисляющая все источники, которым разрешено ставить ваш домен в отправителя конверта.

TXT record at example.com
v=spf1 include:_spf.google.com include:sendgrid.net ip4:203.0.113.10 ~all

Читайте слева направо. Получатель сверяет каждый механизм с IP-адресом подключившегося сервера и останавливается на первом совпадении.

МеханизмТратит запросЧто сопоставляет
ip4: / ip6:НетКонкретный адрес или диапазон CIDR
aДаЗаписи A домена
mxДаАдреса ваших хостов MX
include:ДаSPF-запись другого домена, вычисляемая на месте
exists:ДаСоставленное имя, которое разрешается
allНетВсё остальное, поэтому стоит последним

Квалификатор перед all - это вердикт для всего, что не совпало: -all - жёсткий отказ, ~all - мягкий отказ, ?all - нейтрально, а +all означает «отправлять от моего имени может кто угодно», что хуже, чем отсутствие записи вообще.

Работает ли SPF-запись, решают четыре правила:

  1. Одна запись на домен. Две записи TXT, начинающиеся с v=spf1, дают постоянную ошибку, и всё не проходит. Добавляя провайдера, правьте существующую запись; не создавайте вторую.
  2. Десять DNS-запросов на всё. include, a, mx, exists, ptr и redirect тратят по одному, а каждый include тратит ещё внутри себя. Сверх десяти результат - permerror, который большинство получателей трактуют как отказ. Запись с четырьмя почтовыми провайдерами обычно уже превышает лимит. Заменяйте что можно диапазонами ip4: или, если без этого никак, используйте сервис «сплющивания».
  3. Механизм `ptr` устарел. Уберите его. Он медленный, ненадёжный, и RFC 7208 говорит получателям, что они вправе его пропускать.
  4. SPF не переживает пересылку. Когда университетский алиас пересылает вашу почту дальше, сервер пересылающего подключается к конечному адресату с вашим доменом в конверте, но с адреса, которого вы не перечисляли. SPF не проходит, и сделать с этим ничего нельзя. Это не ошибка вашей настройки: именно поэтому существует DKIM.

Начните с ~all, пока вы ещё выясняете, кто отправляет от вашего имени, и переходите на -all только когда отчёты DMARC покажут, что вы нашли всех.

DKIM: подпись, путешествующая вместе с письмом#

Отправляющий сервер хеширует тело и выбранный набор заголовков, подписывает хеш закрытым ключом и добавляет заголовок DKIM-Signature, называющий домен подписи (d=) и селектор ключа (s=).

a DKIM-Signature header, trimmed
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com;  s=s1; h=from:to:subject:date:message-id; bh=47DEQpj8HBSa...;  b=Xk9pLm2Qv8...

Получатель берёт d= и s=, ищет s1._domainkey.example.com и находит открытый ключ:

TXT record at s1._domainkey.example.com
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...

Что важно на практике:

  • В большинстве случаев вы не создаёте ключ сами. Ключевую пару создаёт ваш почтовый провайдер, оставляет закрытую половину себе и отдаёт вам запись TXT для публикации. Google Workspace, Microsoft 365, Postmark, Mailgun и остальные работают именно так, и у каждого свой селектор.
  • У каждого отправителя свой селектор. Транзакционная почта вашего приложения, рассылки из инструмента кампаний и обычная почта из ящиков - это три разных ключа подписи на трёх разных селекторах, все в одной зоне. Они не конфликтуют.
  • Ключ на 2048 бит не помещается в одну строку DNS. Запись TXT состоит из фрагментов не длиннее 255 символов, поэтому ключ приходится разбивать. Почти каждый редактор DNS делает это молча; некоторые заставляют вас разбивать самому, с кавычками. Если публикация вроде бы прошла, а проверка не проходит, начинайте с этого.
  • DKIM переживает пересылку, пока что-то не изменит сообщение. Список рассылки, который дописывает подвал или добавляет префикс в тему, меняет подписанные байты, и подпись ломается. Именно для этого придумали заголовки ARC, и как отправитель вы на это повлиять не можете.
  • Время от времени меняйте ключи. Опубликуйте новый селектор, переключите на него отправителя, подождите неделю, пока дойдут письма в пути, затем удалите старую запись.

DMARC: политика, связывающая эти два механизма#

DMARC - это одна запись TXT на имени _dmarc под вашим доменом.

TXT record at _dmarc.example.com
v=DMARC1; p=none; rua=mailto:dmarc@example.com; adkim=r; aspf=r; pct=100
ТегПо умолчаниюЧто делает
pобязателенnone, quarantine или reject для домена
spкак pДругая политика для поддоменов
rua-Куда отправлять ежедневные сводные XML-отчёты
ruf-Куда идут отчёты о сбоях по отдельным письмам, если получатель их шлёт
pct100Процент проваливших проверку писем, к которым применяется политика
adkimrAlignment для DKIM: r relaxed, s strict
aspfrAlignment для SPF: r relaxed, s strict

p=none ничего не меняет в доставке. Он лишь просит получателей присылать вам отчёты, что в начале пути именно то, что нужно: поток свидетельств о том, кто отправляет от имени вашего домена, прежде чем вы кому-либо скажете выбрасывать почту. p=quarantine означает папку со спамом. p=reject означает, что принимающий сервер отказывает в сообщении прямо во время SMTP-диалога, так что отправитель получает возврат.

Сводные отчёты на rua - это XML в gzip, по одному на получателя в день, и читать их глазами не предполагается. Скормите их любому парсеру отчётов DMARC - есть бесплатные, - и получите таблицу IP-адресов отправителей, их результатов SPF и DKIM и того, согласовалось ли каждое. В этой таблице вся суть затеи.

Alignment - то, на чём спотыкаются#

Эта идея объясняет любой непонятный результат.

DMARC проходит, когда хотя бы одна из проверок SPF или DKIM проходит и домен, для которого она прошла, совпадает с доменом в видимом заголовке From:. Нужны обе половины. Сообщение может чисто пройти SPF и всё равно не пройти DMARC, и это сочетание - самое частое обращение в поддержку по всей теме.

Вот как так получается. Ваша контактная форма отправляет письма через транзакционного провайдера. Провайдер ставит отправителем конверта bounce@mail.provider.net, потому что возвраты должны приходить к нему. SPF проверяется по provider.net и проходит: их запись в порядке. Но в заголовке From: стоит hello@example.com. provider.net - это не example.com, поэтому эта успешная проверка не согласована и в DMARC ничего не даёт. Если DKIM тоже подписывает с d=provider.net, DMARC проваливается целиком, и ваш p=reject выбрасывает вашу же контактную форму.

Исправление - одно из двух, и любой серьёзный провайдер предлагает оба:

  • Собственный return path. Вы публикуете CNAME, например bounces.example.com, указывающий в инфраструктуру провайдера, они используют его как домен конверта, и SPF теперь проходит для поддомена example.com. При relaxed alignment - он по умолчанию - поддомен согласуется с родительским, и DMARC удовлетворён.
  • DKIM с подписью вашего домена. Вы публикуете селектор провайдера под example.com, и они подписывают с d=example.com. Это надёжнее из двух, потому что оно к тому же переживает пересылку.

Relaxed alignment (r) принимает любой поддомен организационного домена: mail.example.com согласуется с example.com. Strict (s) требует точного совпадения. Оставьте оба relaxed, если нет особой причины, потому что strict ломает собственные return path, не давая на этом уровне никакого значимого выигрыша в безопасности.

Как ввести всё, не потеряв почту#

Порядок важен. Публикация p=reject в первый же день на домене, чьих отправителей вы не инвентаризировали, означает, что ваши счета перестанут приходить, и никто вам об этом не скажет.

  1. Перечислите всех отправителей. Почтовые ящики, контактную форму сайта, сброс паролей приложения, инструмент выставления счетов, CRM, рассылку, службу поддержки, оповещения мониторинга и всё, что кто-то в бухгалтерии настроил три года назад. Этот шаг занимает больше времени, чем все остальные.
  2. Настройте SPF и DKIM для каждого. Одна запись SPF на всех в пределах бюджета из десяти запросов; один селектор DKIM на отправителя, с подписью вашего домена везде, где провайдер это позволяет.
  3. Опубликуйте `p=none` с адресом `rua`. Оставьте на две-четыре недели и читайте отчёты.
  4. Исправьте то, что покажут отчёты. Найдётся отправитель, о котором вы забыли. Он находится всегда.
  5. Переходите на `p=quarantine`. Если хотите более плавный разгон, используйте pct=25, затем pct=100. Следите за отчётами ещё две недели.
  6. Переходите на `p=reject`. Продолжайте читать отчёты. Это не то, что можно закончить.

Два дополнения, о которых полезно знать: принимающую сторону можно укрепить через MTA-STS, который сообщает отправителям, что ваш домен требует TLS, а запись BIMI - та, что выводит логотип рядом с вашими письмами в некоторых клиентах, - учитывается только на доменах, уже стоящих на p=quarantine или p=reject. Ни то ни другое не отправная точка.

Проверка снаружи#

Всё здесь публично, поэтому вы можете проверить всё сами через dig без всяких инструментов.

bash
$ dig TXT example.com +short$ dig TXT s1._domainkey.example.com +short$ dig TXT _dmarc.example.com +short$ dig MX example.com +short

В Windows ту же работу делает Resolve-DnsName example.com -Type TXT. Если только что опубликованная запись не появляется, дело в кэше: полученный вами ответ настолько стар, насколько позволял TTL. Чтобы обойти кэш, спросите свой авторитетный неймсервер напрямую, как описано в статье неймсерверы или DNS-записи, а почему кэш всегда сбивает с толку, объяснено в статье DNS-записи простыми словами.

Вторая половина проверки - отправить письмо на ящик, который вы контролируете, и прочитать заголовки. В Gmail пункт «Показать оригинал» печатает вердикты сверху; в большинстве клиентов ищите заголовок Authentication-Results:

code
Authentication-Results: mx.google.com;  spf=pass (google.com: domain of bounces.example.com designates  203.0.113.10 as permitted sender) smtp.mailfrom=bounces.example.com;  dkim=pass header.i=@example.com header.s=s1;  dmarc=pass (p=NONE sp=NONE dis=NONE) header.from=example.com

Три pass и header.from, совпадающий с вашим доменом, - это готовое состояние. Всё остальное точно говорит, к какой из трёх проверок вернуться.

Что ещё решает судьбу письма в папке спама#

Аутентификация позволяет вас рассматривать. Она не гарантирует доставки. Когда SPF, DKIM и DMARC проходят, в спам письма по-прежнему отправляют вот что:

  • Репутация отправляющего IP и домена. У новых доменов отправки нет ничего, и с ними обращаются осторожно. Отправляйте медленно и ровно в течение нескольких недель, а не десять тысяч писем в первый день.
  • Обратный DNS. У отправляющего IP должна быть запись PTR, разрешающаяся обратно в имя, которое сервер использует в HELO. Общие релеи с этим справляются; сервер, настроенный вами самостоятельно, нередко нет.
  • Доля жалоб. Опубликованный порог Gmail для массовых отправителей - доля жалоб на спам ниже 0,3%, целевой уровень 0,1%. Одна купленная база адресов губит домен на месяцы.
  • Обработка отписки. С 2024 года отправители более 5000 писем в день в Gmail или Yahoo обязаны поддерживать отписку в один клик в рекламной почте и выполнять её в течение двух дней, помимо SPF, DKIM и записи DMARC.
  • Обработка возвратов. Продолжайте писать на адреса с жёсткими возвратами, и вы сообщаете каждому получателю, что не следите за своим списком.
  • Содержимое и ссылки. Сокращённые ссылки, одна большая картинка без текста и домены ссылок с плохой репутацией - всё это идёт вам в минус.

Отправка почты из приложения, которое вы хостите#

Если вашему приложению на Node, Python, PHP или Laravel нужно отправлять сброс паролей и подтверждения заказов, не пытайтесь превратить сам сервер в почтовый. Исходящий порт 25 заблокирован в большинстве сетей, у нового IP нет репутации, а вы взяли бы на себя спам-фильтрацию, очереди и обработку возвратов как побочный проект.

Используйте транзакционного провайдера через SMTP submission - порт 587 с STARTTLS или 465 с неявным TLS - либо его HTTP API. Настройте его с собственным return path и своим селектором DKIM из раздела про alignment выше, чтобы DMARC проходил. Храните учётные данные вне репозитория, в переменных окружения панели, как в статье переменные окружения и секреты; пароль SMTP, закоммиченный в Git, - один из самых быстрых способов заставить ваш домен рассылать спам.

Для RE:NODE эту границу стоит обозначить прямо: мы хостим приложение или сайт, который отправляет письма, на тарифах для приложений и сайтов со слотом обратного прокси, переменными окружения на вкладке Startup и слотами баз данных в панели. Мы не хостим почтовые ящики, не запускаем MX, не ретранслируем SMTP и не хостим зоны DNS. Ваша запись MX указывает на вашего почтового провайдера, запись A указывает на нас, и они независимы, что заодно отвечает на вопрос «сломает ли перенос сайта мою почту». Не сломает, пока вы меняете только записи, относящиеся к сайту. Какие это записи, описано в статье неймсерверы или DNS-записи, а если сам сайт - установка WordPress, то плагин, который обычно и отправляет почту, разобран в статье укрепление безопасности WordPress.

FAQ#

Нужны ли все три записи?

Для DMARC нужно, чтобы хотя бы одна из проверок SPF или DKIM прошла согласованно, так что строго говоря можно обойтись двумя. На практике публикуйте все три: SPF ломается при пересылке, DKIM ломается на списках рассылки, и когда есть обе, одна обычно выживает. DMARC без них обеих бессмыслен.

Остановит ли запись DMARC подделку моего домена?

Она останавливает письма, выдающие себя за ваш домен, у получателей, соблюдающих DMARC, а это теперь каждый крупный почтовый провайдер. Она ничего не делает против похожих доменов, подмены отображаемого имени и взломанного аккаунта, отправляющего подлинно аутентифицированную почту. Она закрывает одну дверь, и это самая часто используемая дверь.

Почему моя почта проходит SPF, но не проходит DMARC?

Alignment. SPF прошёл для домена конверта, который принадлежит вашему провайдеру отправки, а не для домена в видимом заголовке From:. Настройте собственный return path на поддомене вашего домена или пусть провайдер подписывает DKIM вашим доменом, и проверка станет согласованной.

Через сколько изменение вступит в силу?

Через время TTL записи плюс то время, сколько получатели держат её в кэше. За день до любых изменений снизьте TTL до 300 секунд и после них ждите, пока истечёт прежний. В аутентификации почты ничего не происходит мгновенно.

Можно ли хостить почту на том же тарифе, что и сайт?

Здесь - нет. RE:NODE хостит сайт или приложение; почтовые ящики, MX и ретрансляция SMTP - чужая услуга. Такое разделение нормально и к тому же удобно: сайт можно переносить между хостингами, не трогая ни одной почтовой записи.

Что на самом деле делает p=none?

Ничего с вашей доставкой. Он просит получателей сообщать, что они увидели, и не предпринимать действий по сбоям. Это правильный первый шаг, а оставлять домен на p=none годами всё равно лучше, чем перескочить на p=reject и обнаружить своих отправителей одновременно с клиентами.


Комментарии

Полностью анонимно: без аккаунта, без почты, без cookie. Мы храним имя, которое вы ввели, текст и время - больше ничего. Количество ссылок ограничено, разметка не отображается.

0/2000