DKIM

DKIM (DomainKeys Identified Mail, RFC 6376) — механизм криптографической подписи писем ключом отправляющего домена. Получатель извлекает публичный ключ из DNS отправителя, проверяет подпись. Гарантирует что письмо (заголовки + тело) не изменено и что отправитель имеет private-key.

Как работает

  1. Отправитель имеет RSA/Ed25519 пару ключей.
  2. Публичный ключ публикуется в DNS: selector._domainkey.example.com TXT "v=DKIM1; k=rsa; p=<base64 public key>".
  3. При отправке письма MTA вычисляет хеш заголовков (выбранных) + тела. Подписывает приватным ключом.
  4. В заголовки письма вставляется DKIM-Signature: ....
  5. Получатель:
    1. Достаёт publkey по selector._domainkey.<domain>.
    2. Пересчитывает хеш заголовков и тела.
    3. Проверяет подпись.

Заголовок DKIM-Signature

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed;
        d=example.com; s=mail; t=1700000000;
        h=from:to:subject:date:message-id;
        bh=iNP0PMSMjWXhArAWNlrsH23Uw6Y9wTC1YMDHFR7XR2Y=;
        b=cD8DfsQiYK7...==
ТегЗначение
v=Версия (1)
a=Алгоритм: rsa-sha256, ed25519-sha256
c=Canonicalization: simple/relaxed для заголовков и тела
d=Signing domain
s=Selector (произвольная строка, чтобы можно было иметь несколько ключей)
t=Timestamp
h=Список подписанных заголовков
bh=Body hash (base64)
b=Signature (base64)

Canonicalization

Проблема: MTA'ы на пути переписывают заголовки (добавляют пробелы, меняют регистр). Простой SHA-256 всегда бы ломался. DKIM определяет два режима нормализации перед хешем:

Обычно все используют relaxed/relaxed.

Selector

Selector — произвольная строка в DNS. Одному домену можно иметь много ключей одновременно:

mail._domainkey.example.com    TXT "v=DKIM1; k=rsa; p=..."   ← основной
backup._domainkey.example.com  TXT "v=DKIM1; k=rsa; p=..."   ← резерв
sendgrid._domainkey.example.com TXT "v=DKIM1; k=rsa; p=..."  ← через SendGrid

Ротация: делаешь новый selector с новым ключом, ждёшь пока трафик переползёт, старый удаляешь.

Что подписывается

Только заголовки перечисленные в h= и тело. Обычно:

h=from:to:cc:subject:date:message-id:mime-version:content-type

Заголовки не в списке — можно добавлять/менять по пути, подпись всё равно верна. Тело — всё, полностью. Одно исключение — l= tag ограничивает подписанную длину тела (deprecated из-за XSS-подобных атак).

Размеры ключей

АлгоритмРазмерПодходит для DNS
RSA-1024128 байт publkeyУстарело, но многие ещё используют
RSA-2048256 байтСтандарт сейчас. Длинная TXT — многострочная
RSA-4096512 байтOverkill
Ed2551932 байтаИдеально, но поддержка не 100%

Многострочная TXT для RSA-2048

TXT-запись имеет предел 255 байт на строку. RSA-2048 не влезает — разбивается на несколько строк:

selector._domainkey.example.com. IN TXT (
    "v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA..."
    "...длиннющий_ключ_дальше..."
    "...ещё_кусок..."
)

Резолверы склеивают эти строки.

Настройка на Postfix / OpenDKIM

# Установка
apt install opendkim opendkim-tools

# Генерация ключа
opendkim-genkey -s mail -d example.com -b 2048
# создаёт mail.private (приватный) и mail.txt (что положить в DNS)

# /etc/opendkim.conf
Domain          example.com
KeyFile         /etc/opendkim/keys/example.com/mail.private
Selector        mail
Socket          inet:8891@localhost

# /etc/postfix/main.cf
milter_default_action = accept
smtpd_milters = inet:localhost:8891
non_smtpd_milters = inet:localhost:8891

Что не защищает DKIM

DKIM + SPF + DMARC

Три работают вместе — DMARC объединяет.

Отдельно каждый:

См. также