DKIM
DKIM (DomainKeys Identified Mail, RFC 6376) — механизм криптографической подписи писем ключом отправляющего домена. Получатель извлекает публичный ключ из DNS отправителя, проверяет подпись. Гарантирует что письмо (заголовки + тело) не изменено и что отправитель имеет private-key.
Как работает
- Отправитель имеет RSA/Ed25519 пару ключей.
- Публичный ключ публикуется в DNS:
selector._domainkey.example.com TXT "v=DKIM1; k=rsa; p=<base64 public key>". - При отправке письма MTA вычисляет хеш заголовков (выбранных) + тела. Подписывает приватным ключом.
- В заголовки письма вставляется
DKIM-Signature: .... - Получатель:
- Достаёт publkey по
selector._domainkey.<domain>. - Пересчитывает хеш заголовков и тела.
- Проверяет подпись.
- Достаёт publkey по
Заголовок 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 определяет два режима нормализации перед хешем:
- simple — минимально: убирает trailing CRLF в теле. Заголовки не трогает. Ломается легко.
- relaxed — сжимает whitespace, lowercases имена заголовков, убирает trailing whitespace. Устойчивее.
Обычно все используют 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-1024 | 128 байт publkey | Устарело, но многие ещё используют |
| RSA-2048 | 256 байт | Стандарт сейчас. Длинная TXT — многострочная |
| RSA-4096 | 512 байт | Overkill |
| Ed25519 | 32 байта | Идеально, но поддержка не 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
- Replay-атаки. Захватил письмо → пересылаешь копию другому получателю. Подпись валидна. Именно поэтому DKIM обычно с DMARC.
- Спам. DKIM говорит «письмо от example.com», не «письмо не спам». Спамер может подписать своим доменом.
- Тайминг. Подпись остаётся валидной хоть десять лет —
t=опционален. Нет embed expiration.
DKIM + SPF + DMARC
Три работают вместе — DMARC объединяет.
Отдельно каждый:
- SPF — «этот IP имеет право».
- DKIM — «письмо подписано».
- DMARC — «SPF или DKIM должен пройти, и выравниваться с From-заголовком».