RE:NODE

Приложения11 мин чтения

DKIM-ключи и их ротация: селекторы, длина ключа, публикация

Как устроены DKIM-ключи и селекторы, RSA 2048 против Ed25519, публикация длинных ключей в DNS и плановая ротация ключей без поломки писем в пути.

0 прочтений

DKIM-ключ - это пара ключей: закрытая половина остаётся на почтовом сервере и подписывает каждое исходящее письмо, открытая лежит в DNS под именем, которое называется селектором, и получатели забирают её оттуда, чтобы проверить подпись. Используйте 2048-битный RSA-ключ: 1024 - минимум, который стандарт ещё допускает, и достаточным он больше не считается, а 4096 создаёт проблемы с DNS без практической выгоды. Если ваш сервер умеет подписывать двумя ключами, добавьте рядом ключ Ed25519. Ротация выглядит так: публикуете новый селектор, переключаете подпись на него и оставляете старый открытый ключ в DNS как минимум на неделю, прежде чем удалить, потому что письма, подписанные старым ключом, продолжают проверяться и после того, как вы перестали им пользоваться. Вот и вся процедура; остальная часть статьи - подробности, благодаря которым она проходит гладко.

Теория - зачем существует DKIM и как он связан с SPF и DMARC - изложена в статье SPF, DKIM и DMARC простыми словами. Здесь предполагается, что вы знаете, для чего нужен DKIM, и хотите правильно управлять ключами.

Что содержит подпись#

Когда ваш сервер подписывает письмо, он добавляет такой заголовок:

заголовок DKIM-Signature
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com;  s=s2026a; t=1791446400; h=from:to:subject:date:message-id:mime-version;  bh=frcCV1k9oG9oKj3dpUqdJg1PxRT2RSN/XKdLCPjaYaY=;  b=Ys9Bd7pLq2T...
ТегЗначение
d=Подписывающий домен - тот, который DMARC сравнивает с From:
s=Селектор - какой ключ забирать
a=Алгоритм: rsa-sha256 или ed25519-sha256
c=Канонизация для заголовков/тела: relaxed терпит изменения пробелов
h=Подписанные заголовки по порядку
bh=Хэш тела письма
b=Сама подпись
t= / x=Время подписи и необязательный срок действия

Получатель берёт s= и d=, составляет имя s2026a._domainkey.example.com, забирает оттуда TXT-запись и проверяет b= открытым ключом. Если проверка проходит и хэш тела по-прежнему совпадает с bh=, подпись проходит.

Две настройки подписывающей стороны стоит проверить. Используйте канонизацию relaxed/relaxed: simple ломается от безобидных изменений пробелов, которые вносят серверы по пути. И убедитесь, что From: входит в h=, как требует стандарт; хорошие подписывающие программы указывают его дважды («oversigning»), чтобы атакующий не мог добавить второй заголовок From:, не сломав подпись.

Избегайте тега l=, если ваш сервер его предлагает. Он ограничивает подписанную часть тела числом байт, чтобы добавленные позже подписи внизу письма не ломали DKIM, - а значит, кто угодно может дописать что-то к вашему подписанному письму, и оно всё равно пройдёт проверку.

Селекторы и зачем их несколько#

Селектор - это просто метка, позволяющая одному домену публиковать сразу много ключей. У каждой отправляющей системы свой:

несколько селекторов в одной зоне
s2026a._domainkey       IN TXT    "v=DKIM1; k=rsa; p=MIIBIjANBgkq..."s2026e._domainkey       IN TXT    "v=DKIM1; k=ed25519; p=11qYAYKxCrfVS/7TyWQHOg7hcvPapiMlrwIaaPcHURo="pm._domainkey           IN TXT    "v=DKIM1; k=rsa; p=MIGfMA0GCSqG..."selector1._domainkey    IN CNAME  selector1-example-com._domainkey.provider.example.

Первые две - ключи RSA и Ed25519 вашего собственного почтового сервера. Третья принадлежит транзакционному сервису. Четвёртая - CNAME в зону провайдера: некоторые провайдеры (известный пример - Microsoft 365) просят направить их селекторы через CNAME, чтобы менять ключи на своей стороне, не прося вас трогать DNS.

Селекторы ничего не стоят и не конфликтуют друг с другом, так что называйте их так, чтобы через год было понятно, что это. Распространённые схемы кодируют дату (s2026a, 202610), систему (mail, app) или и то и другое. Свежие версии Stalwart генерируют селекторы по шаблону, который по умолчанию выглядит как v1-rsa-20261008 - версия, алгоритм, дата; это хорошая схема, чтобы перенять её, если вы генерируете ключи сами.

Типы и длина ключей#

КлючРазмер DNS-записиПоддержка получателямиПрименение
RSA 1024Умещается в одну TXT-строкуПовсеместнаяТолько унаследованный; замените
RSA 2048Две TXT-строкиПовсеместнаяВыбор по умолчанию
RSA 4096Большой; проблемы с размером UDPПовсеместная в теорииНе стоит того
Ed25519КрошечныйЧастичнаяВместе с RSA, никогда вместо

RFC 8301 говорит, что подписывающие стороны обязаны использовать RSA-ключи не короче 1024 бит и должны использовать не короче 2048; проверяющие не должны считать действительными ключи короче 1024 бит. 1024-битный RSA доступен хорошо финансируемым атакующим, и некоторые крупные получатели считают его слабым. Разумный минимум - 2048.

Ключи на 4096 бит на практике не полезнее. Один только открытый ключ занимает около 700 символов base64, TXT-ответ может превысить традиционный предел UDP DNS в 512 байт, а редакторы некоторых DNS-провайдеров не умеют его хранить. Резолверы могут переключиться на TCP, но вы добавляете способы всё сломать ради безопасности, которая пока никому не нужна.

Ed25519 (RFC 8463) - современный выбор: короткие ключи, короткие подписи, быстро. Проблема в поддержке проверки: не каждый крупный получатель проверяет подписи Ed25519, а подпись, которую получатель не понимает, игнорируется, а не считается проваленной. Поэтому на практике используют двойную подпись: каждое письмо подписывается и RSA-ключом, и ключом Ed25519. Получатели, понимающие Ed25519, могут использовать его; все остальные используют RSA. Stalwart делает так по умолчанию для новых доменов.

Публикация ключей в DNS#

Формат записи:

code
v=DKIM1; k=rsa; p=<base64 public key>

v=DKIM1 необязателен, но принят, и если он есть, то должен стоять первым. k= по умолчанию равен rsa; ключам Ed25519 нужен k=ed25519. p= содержит сам ключ. Необязательные теги: t=y помечает, что домен тестирует DKIM (получатели не должны обращаться с провалами иначе, чем с неподписанной почтой, - уберите его, когда всё заработает), а t=s требует, чтобы d= в точности совпадал с идентификатором, а не был поддоменом.

2048-битный RSA-ключ занимает около 400 символов base64, а одна TXT-строка DNS вмещает не больше 255. Поэтому запись хранится в виде нескольких строк, которые получатели склеивают:

2048-битный ключ, разбитый на строки
s2026a._domainkey IN TXT ( "v=DKIM1; k=rsa; "    "p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAx3...first part..."    "...second part...IDAQAB" )

Большинство веб-редакторов DNS разбивают длинные значения автоматически. Некоторые требуют вставить строки в кавычках самостоятельно; немногие молча обрезают значение. После публикации запросите запись и сравните:

bash
$ dig +short TXT s2026a._domainkey.example.com"v=DKIM1; k=rsa; " "p=MIIBIjANBgkq...first..." "...second...IDAQAB"

Несколько строк в кавычках в ответе - это нормально. Ключ короче того, что выдал ваш сервер, или с пробелами и кавычками внутри base64 - нет.

Если вы генерируете ключи вручную, а не поручаете это почтовому серверу, с задачей справляется openssl:

bash
# RSA 2048: private key, then the public key as DKIM wants it$ openssl genrsa -out s2026a.private 2048$ openssl rsa -in s2026a.private -pubout -outform der | openssl base64 -A# Ed25519: DKIM wants the raw 32-byte public key, not the DER wrapper$ openssl genpkey -algorithm ed25519 -out s2026e.private$ openssl pkey -in s2026e.private -pubout -outform der | tail -c 32 | openssl base64 -A

Ротация ключей без поломки почты#

Зачем вообще менять ключи: утёкший закрытый ключ позволяет кому угодно подписывать письма от имени вашего домена, пока вы не заметите; старые ключи, как правило, слабые; а сотрудники, у которых когда-то был доступ к серверу, уходят. Часто цитируемая отраслевая рекомендация (от M3AAWG) - менять ключи как минимум раз в полгода. Многие организации делают это раз в год; надёжная ежегодная ротация лучше ежеквартальной на бумаге.

Безопасная последовательность:

  1. Сгенерируйте новый ключ под новым селектором. Никогда не используйте имя селектора повторно для другого ключа - получатели и резолверы кэшируют старую запись на время её TTL, и подписи новым ключом будут проваливаться при проверке по закэшированному старому.
  2. Опубликуйте новый открытый ключ и проверьте его снаружи через dig. Подождите как минимум TTL записи, чтобы её увидел каждый резолвер.
  3. Переключите подпись на новый ключ. С этого момента новые письма несут s= с новым селектором.
  4. Оставьте старый открытый ключ опубликованным как минимум на семь дней. Письма, подписанные старым ключом, могут лежать в очередях, отправляться повторно, пересылаться или проверяться получателем с опозданием при повторном сканировании.
  5. Отзовите старый ключ. Либо удалите запись, либо опубликуйте её с пустым ключом (v=DKIM1; p=), что RFC 6376 определяет как отзыв. Пустой p= чуть информативнее для того, кто будет потом что-то отлаживать.
  6. Уничтожьте старый закрытый ключ, как только убедитесь, что им больше ничто не подписывает.
Новый ключновый селекторПубликация ключаждём один TTLПодпись новым ключомСтарый ключ в DNS7 дней и большеОтзыв старого ключапустой p= или удаление
Ротация DKIM от начала до конца

Типичная ошибка - сделать шаги 3 и 5 одновременно, то есть переключить подпись и сразу удалить старую запись; тогда проваливается каждое письмо, ещё находящееся в пути. Другая - сделать шаг 3 раньше, чем шаг 2 успел устояться; тогда проваливается почта за первый час.

Автоматическая ротация в Stalwart

Свежие версии Stalwart умеют сами управлять этим жизненным циклом: у каждого домена есть настройка управления DKIM, которая бывает ручной или автоматической. Автоматический режим меняет ключи по расписанию (по умолчанию раз в 90 дней), держит DNS-запись заменённого ключа опубликованной ещё 7 дней после переключения и удаляет сам ключ ещё через 30 дней. Для этого нужно автоматическое управление DNS - доступ к API вашего DNS-провайдера, - потому что публиковать и затем удалять записи он должен сам. Без этого ротация остаётся ручной: создайте новую подпись в веб-админке, опубликуйте её запись, переключитесь, а позже удалите старую, следуя последовательности выше. Детали здесь зависят от версии, так что сверьтесь с документацией к релизу, который у вас работает.

Экстренная ротация после утечки#

Расписание выше - для плановой ротации. Если закрытый ключ мог утечь - взлом сервера, резервная копия, оставленная в публичном бакете, бывший администратор, сохранивший себе копию, - порядок меняется, потому что теперь опасность представляет старый ключ.

  1. Сгенерируйте и опубликуйте новый ключ под новым селектором, как и раньше, и переключите подпись на него, как только запись начнёт разрешаться через публичный резолвер. Не ждите полного TTL: минуты сейчас важнее нескольких проваленных подписей.
  2. Немедленно отзовите старый ключ пустым p=. Часть легитимной почты, ещё находящейся в пути, провалит DKIM. Это цена, и она невелика: SPF для такой почты обычно всё равно проходит, а DMARC нужна лишь одна выровненная проходящая проверка.
  3. Просматривайте агрегированные отчёты DMARC в последующие недели. Письма, подписанные старым селектором с незнакомых вам IP-адресов, - доказательство того, что ключом воспользовались.
  4. Выясните, как произошла утечка, прежде чем следующий ключ уйдёт тем же путём. Ротация не поможет, если новый закрытый ключ лежит в том же открытом месте.

Каждый, кто управляет доменом, должен знать, где лежат его закрытые DKIM-ключи и кто может их прочитать. Если ответ звучит как «в архиве резервной копии, который могут скачать несколько человек», обращайтесь с этим архивом так же бережно, как с самим ключом.

Ключи у других отправителей#

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

  • Менять их ключи сами вы не можете, если провайдер этого не предлагает. Многие провайдеры меняют ключи на своей стороне через описанную выше схему с CNAME, и это хороший повод предпочесть CNAME, когда его предлагают.
  • Удаляйте селекторы сервисов, которыми перестали пользоваться. Ключ, закрытая половина которого принадлежит поставщику, которому вы больше не платите, всё ещё может подписывать действительную почту от вашего домена. Удалите запись, когда закончится контракт.
  • Ведите учёт. Короткий список «селектор, система, владелец, дата публикации» экономит полдня раскопок с dig. Составить его помогают отчёты DMARC: каждая проходящая DKIM-подпись в них называет свой селектор.
  • Следите за провайдерами, подписывающими своим доменом. Если сервис подписывает с d=provider.example, а не вашим доменом, его подпись проходит, но не выравнивается, и DMARC для этой почты будет зависеть только от SPF. Большинство сервисов умеют подписывать вашим доменом, как только вы опубликуете их запись.

Когда DKIM не проходит#

Заголовок Authentication-Results полученного письма называет причину сбоя:

  • `dkim=fail (body hash did not verify)` - тело письма изменилось после подписи. Подпись списка рассылки внизу письма, шлюз безопасности, переписывающий ссылки, антивирус, добавляющий уведомление, или канонизация тела simple, столкнувшаяся с изменением пробелов.
  • `dkim=fail (signature verification failed)` - изменились заголовки, или опубликованный ключ не соответствует закрытому. После ротации это обычно значит, что опубликована не та запись.
  • `dkim=neutral` или `permerror (no key)` - получатель не нашёл ключ по селектору. Проверьте, не задвоен ли домен в имени и совпадает ли селектор в s= с тем, что вы опубликовали.
  • `dkim=permerror (key syntax)` - запись некорректна: нет p=, лишние кавычки внутри base64 или обрезанный ключ.
  • `dkim=pass`, но `dmarc=fail` - подпись прошла для домена, который не выравнивается с From:, часто для собственного домена провайдера. О выравнивании - в статье отчёты DMARC и поэтапное ужесточение политики.

То, что списки рассылки ломают подписи, - ожидаемо, и исправлять это не вам; заголовки ARC (Authenticated Received Chain) существуют для того, чтобы списки могли поручиться за исходный результат.

DKIM на RE:NODE Mail Server#

Линейка Mail Server работает на Stalwart, и его веб-админка генерирует DKIM-ключи для каждого добавленного домена и показывает записи для публикации. Публикуете вы их там, где размещён ваш DNS, - редактора зон на нашей стороне нет, - вместе с MX, SPF и DMARC. Ключи хранятся в хранилище данных сервера, так что слоты резервных копий вашего тарифа (от одного до четырёх, в зависимости от уровня) покрывают их вместе с ящиками. Если вы отправляете почту через relay, настроенный в веб-админке, Stalwart всё равно сначала подписывает её вашими ключами. Как добавить домен и прочитать список записей, рассказано в статье настройка почтового сервера Stalwart.

FAQ#

Какую длину DKIM-ключа использовать?

RSA 2048. Он поддерживается повсеместно и помещается в DNS при обычном разбиении на строки. Если сервер умеет подписывать дважды, добавьте рядом Ed25519. Не используйте 1024 для новых ключей и избегайте 4096 - он создаёт проблемы с DNS без практической пользы.

Можно ли иметь больше одной DKIM-записи?

Да, по одной на селектор, сколько угодно. У каждой отправляющей системы должен быть свой. Чего нельзя - так это двух разных ключей под одним именем селектора.

Сколько держать старый ключ после ротации?

Как минимум семь дней после того, как вы перестали им подписывать, и дольше, если вы отправляете почту, которая может какое-то время повторно отправляться или пересылаться. Затем отзовите его пустым p= или удалите.

Нужно ли менять ключи, если ничего не утекло?

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

Почему у моей подписи Ed25519 в Gmail нет результата?

Потому что проверяют Ed25519 пока не все получатели. Нераспознанная подпись игнорируется, а не проваливается. Именно поэтому вы подписываете ещё и RSA - результат даёт RSA-подпись.


Комментарии

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

0/2000