SPF ломается при пересылке — и это нормально: SRS, ARC и почему DMARC надо держать на DKIM
SPF физически не переживает пересылку — это не ошибка конфигурации, а конструктивное свойство протокола. Разбираем, почему опора на SPF-выравнивание в DMARC — ошибка проектирования, и как собрать схему, которая не разваливается при первом же пересланном письме: DKIM как якорь, SRS на форвардере, ARC через промежуточные хопы.
EvilMail Team19 июля 2026 г.12 мин чтения
Типичный тикет в поддержку почтовика выглядит так: «Настроил SPF с -all, DMARC на p=reject, mail-tester показывает честные 10/10. Но письма с алиаса info@ на мой личный Gmail стабильно летят в спам с dmarc=fail. Что не так с настройками?»
С настройками всё так. Виноват не Gmail и не ваша SPF-запись. Виновато то, что письмо переслали. И пока вы будете крутить ~all против -all и добавлять include:, симптом никуда не денется — потому что вы лечите не ту болезнь. SPF проверяет вообще не того отправителя, о котором вы думаете, и при пересылке он обязан падать. Всегда. По дизайну.
SPF проверяет конверт, а не то, что видит пользователь
Почему SPF ломается при пересылке: SRS, ARC и DKIM для DMARC — EvilMail Blog
В любом SMTP-письме есть два разных «отправителя», и их постоянно путают.
Первый — RFC5321.MailFrom, он же envelope-from, он же Return-Path. Это то, что сервер говорит в команде MAIL FROM во время SMTP-диалога. Пользователь этого адреса никогда не видит.
Второй — RFC5322.From, заголовок From: внутри самого письма. Именно его рисует почтовый клиент как «от кого».
Эти два адреса могут отличаться, и в реальном мире отличаются постоянно. Смотрим на сырой диалог:
SPF смотрит только на MAIL FROM:<[email protected]> и на IP сервера, который открыл соединение. Заголовок From: [email protected] для SPF не существует вообще — протокол его не читает и не проверяет. Отсюда растёт весь корень проблемы: SPF привязан к паре «домен конверта + сетевой адрес», а не к тому, что показано человеку.
Почему пересылка гарантированно роняет SPF
Разберём по шагам, что происходит с алиасом [email protected], который настроен на форвард в Gmail.
1.Оригинальный сервер shop.com (IP 203.0.113.5, честно прописан в SPF) отправляет письмо с MAIL FROM:<[email protected]>.
2.Письмо приходит на ваш форвардер forward.example (IP 198.51.100.7).
3.Форвардер переотправляет письмо на mail.google.com, сохраняя исходный Return-Path[email protected], но отправляет-то он со своего IP 198.51.100.7.
4.Gmail берёт домен из Return-Path — shop.com — тянет его SPF-запись и спрашивает: «есть ли 198.51.100.7 среди разрешённых для shop.com?». Нет. → spf=fail.
Это не сбой. SPF по определению завязан на сетевой путь, а пересылка этот путь меняет. Никакая запись на стороне shop.com не может перечислить IP-адреса всех форвардеров мира, через которые кто-то решит прогнать письмо.
Попутно два момента, которые любят добавлять шума:
Лимит 10 DNS-запросов. SPF-запись раскрывается рекурсивно, и если ваши include: тянут за собой больше 10 lookup-ов, вы получаете permerror — это не fail, но DMARC трактует его так же плохо.
`~all` против `-all`.~all — это softfail, -all — hardfail. При пересылке разница косметическая: письмо в любом случае приходит с чужого IP и в любом случае не выравнивается по SPF.
DMARC: выравнивание проходит, если сошёлся SPF ИЛИ DKIM
Здесь и кроется решение. DMARC даёт pass, если проходит хотя бы один из двух механизмов — причём не просто «валидно», а с выравниванием (alignment).
Выравнивание — это совпадение домена из видимого From: с доменом, который прошёл проверку:
для SPF сверяется домен Return-Path с доменом From;
для DKIM сверяется домен из тега d= подписи с доменом From.
Строгость задают теги adkim и aspf:
relaxed (значение r) — достаточно совпадения на уровне организационного домена (mail.shop.com ≈ shop.com);
strict (значение s) — требуется точное совпадение.
Отсюда главный вывод. При пересылке SPF всегда fail и заведомо не aligned. Значит, реально работающий DMARC при пересылке держится исключительно на DKIM. SPF в контексте DMARC — приятный бонус для прямой доставки, но не фундамент. Строить p=reject в расчёте на SPF-выравнивание — это ошибка проектирования, которая вскрывается на первом же пересланном письме.
Почему DKIM переживает пересылку (а иногда нет)
DKIM подписывает тело письма и набор выбранных заголовков криптографической подписью в домене d=. Подпись не зависит от IP-адреса — форвардер может слать откуда угодно, хоть с мобильного модема, и подпись останется валидной, пока никто не тронул подписанные части.
«Пока не тронул» — и есть слабое место. DKIM ломается, когда по дороге меняют то, что попало под подпись:
рассылки (Mailman и подобные) дописывают [list-name] в Subject и футер с отпиской в тело;
антивирус-прокси и «безопасные ссылки» переписывают URL внутри письма;
некоторые ESP пересобирают MIME и меняют кодировку заголовков.
Практика подписания, которая максимально повышает выживаемость:
Подписывать From, Subject, Date, To и MIME-заголовки (Content-Type, MIME-Version). From обязателен — без него подпись бессмысленна для DMARC.
Canonicalization ставить relaxed/relaxed. simple/simple разваливается от любого лишнего пробела или переноса, добавленного транзитным сервером; relaxed терпит нормализацию пробелов.
Никогда не использовать тег `l=`. Он ограничивает подпись первыми N байтами тела — это дыра: злоумышленник дописывает произвольный контент после подписанной части, и подпись остаётся валидной.
Ключ минимум 2048 бит. 1024 давно считается слабым, а публиковать его в DNS — самому себе создавать проблему.
Держать несколько селекторов и ротировать их — чтобы выкатить новый ключ, не роняя старые письма в полёте.
SRS: чините конверт на СВОём форвардере
Теперь другая сторона. Когда пересылаете вы, чинить Return-Path — ваша обязанность. Иначе вы сами отправляете письмо с MAIL FROM:<[email protected]> со своего IP, и следующий сервер справедливо считает вас спуфером shop.com.
Решение — SRS (Sender Rewriting Scheme). Он переписывает envelope-from так, что владельцем конверта становится ваш домен: SPF снова проходит на вашей стороне, а bounce-сообщения корректно возвращаются обратно к оригинальному отправителю. Формат переписанного адреса:
Здесь HHH — криптографический хэш (защита от использования вас как открытого релея для баунсов), TT — таймстемп, дальше закодированы исходный домен и локальная часть. Для цепочек пересылок (письмо форвардится повторно) используется префикс SRS1.
Рабочая связка — postsrsd (демон слушает порт 10001 для sender и 10002 для recipient) плюс Postfix. В main.cf:
Критично не забыть: SRS-домен нужно добавить в собственную SPF-запись форвардера — иначе переписанный конверт ...=forward.example сам провалит SPF.
Важная оговорка, чтобы не было ложных ожиданий: SRS чинит SPF только для следующего хопа и не помогает DMARC-выравниванию исходного домена. Это инструмент про корректные баунсы и про то, чтобы вас самих не приняли за спуфера, — а не про то, чтобы shop.com прошёл DMARC. Тот по-прежнему проходит только через DKIM.
ARC: сохранить вердикт через промежуточные хопы
Остаётся сценарий, который SRS не закрывает: DKIM убила рассылка или прокси, письмо честное, но приёмник видит dkim=fail и не знает, что на входе к форвардеру всё было валидно.
Для этого есть ARC (Authenticated Received Chain, RFC 8617). На каждом хопе он добавляет три заголовка:
ARC-Authentication-Results — что именно этот сервер увидел на входе (spf=pass, dkim=pass и т.д.);
ARC-Message-Signature — подпись сообщения в момент прохождения;
ARC-Seal — печать, связывающая всю цепочку предыдущих ARC-заголовков.
Конечный приёмник, который доверяет форвардеру, может принять письмо на основании ARC, даже если текущие DKIM/SPF провалились. Ключевое слово — «доверяет»: ARC не серебряная пуля, а механизм поручительства. Он работает, только если приёмник ведёт репутацию того, кто ставил печать. Google и Microsoft это делают; ваш свежий форвардер для них никто, и его ARC-печать поначалу не имеет веса.
Настройка на выходе форвардера — rspamd, модули arc и dkim_signing:
Отдельная история — рассылки под p=reject. Если управлять ARC на принимающей стороне вы не можете, альтернатива — From-munging: Mailman переписывает заголовок From на адрес самого списка ([email protected] вместо автора). Выглядит уродливо, ломает «ответить автору», но спасает доставку под жёсткими DMARC-политиками.
Диагностика: читаем Authentication-Results, а не гадаем
Прежде чем что-то менять, посмотрите, что реально опубликовано и что видит приёмник.
Дальше — самое важное. Откройте доставленное письмо и прочитайте заголовок Authentication-Results. Для правильно настроенной пересылки он выглядит так:
text
Authentication-Results: mx.google.com;
spf=fail (google.com: domain of [email protected] does not designate
198.51.100.7 as permitted sender) [email protected];
dkim=pass header.d=shop.com;
dmarc=pass (p=REJECT sp=REJECT) header.from=shop.com
spf=fail — и это нормально, письмо переслали. dkim=pass header.d=shop.com — подпись доехала. dmarc=pass — сработало DKIM-выравнивание. Такое письмо доставится даже при p=reject.
Как отличить «SPF упал из-за пересылки» от реального спуфинга? Смотрите на header.d (DKIM) и header.from (DMARC). Если dkim=pass, а header.d совпадает с header.from — это ваше письмо, просто прошедшее через форвардер. Если DKIM тоже fail или d= от чужого домена, а header.from ваш, то кто-то действительно подделывает ваш адрес, и p=reject отработал ровно так, как задумано.
Чеклист перед тем, как ставить p=reject
DKIM подписан relaxed/relaxed, ключ 2048 бит, без тега `l=`, под подписью From/Subject/Date/MIME-заголовки.
DMARC вводить постепенно: p=none; rua=mailto:[email protected], читать агрегатные отчёты 2–4 недели, затем quarantine, и только потом reject.
adkim и aspf держать relaxed, пока не убедитесь на 100%, что вся легитимная почта выравнивается при строгом режиме.
На всех своих форвардерах включён postsrsd, а SRS-домен добавлен в SPF-запись форвардера.
На форвардерах, которые модифицируют письмо (рассылки, антивирус-прокси), включён ARC в rspamd.
В SPF ставить -all только когда весь легитимный исходящий трафик учтён; уложиться в 10 DNS-lookup, иначе permerror.
Мониторить rua-отчёты на предмет источников с fail — это и забытые сервисы, и попытки спуфинга.
SPF стоит воспринимать как проверку прямого пути — она честно работает, пока письмо идёт напрямую, и честно ломается, как только путь меняется. DKIM — это переносимая идентичность, которая едет вместе с письмом и остаётся якорем DMARC через любые хопы. SRS и ARC — ремонтные инструменты на границах, где путь ломается: один чинит конверт для следующего сервера, другой сохраняет исходный вердикт, когда содержимое всё-таки тронули. В инфраструктуре evilmail.pro исходящая почта и форвардинг — включая временные ящики — устроены ровно по этой схеме: DKIM как основа, SRS на форвардерах, ARC там, где письмо переписывается по дороге.