Сообщение об ошибке
dkim=fail (body hash did not verify)
Также выводится как
- dkim=neutral (body hash did not verify)
mailauth пишет `dkim=fail (body hash did not verify)` в строгом режиме и `dkim=neutral (body hash did not verify)` в мягком режиме, который используется по умолчанию (оба варианта воспроизведены на версии 8.0.1). OpenDKIM сообщает о той же ситуации как `dkim=fail reason="signature verification failed"` (воспроизведено на версии 2.11.0).
DKIM verifiers (Gmail, mailauth, Rspamd…) — Authentication-Results after the body changed in transit
Что это означает
Тег `bh=` в DKIM-подписи содержит хеш тела письма в том виде, в каком его подписал отправитель. Получатель вычислил хеш полученного тела (после канонизации) и получил другое значение, значит, тело изменилось по пути. Подпись заголовков тут даже ни при чём: по RFC 6376 несовпадение хеша тела — постоянная ошибка. Типичные виновники — приписки списков рассылки в конце письма, дисклеймеры и шлюзы безопасности, которые переписывают ссылки.
Почему это происходит
В конец письма добавлена приписка или дисклеймер
частоСписки рассылки (приписка со ссылкой для отписки), корпоративные дисклеймеры, которые добавляет шлюз исходящей почты, или строки «Sent from…», добавленные почтовыми ретрансляторами, изменяют канонизированное тело.
Шлюз безопасности переписал тело письма
частоПереписывание ссылок (Safe Links, URL defense), удаление вложений или вставка баннеров фильтрами входящей или исходящей почты.
Изменилась кодировка передачи MIME
иногдаРетранслятор без поддержки 8BITMIME преобразует 8-битное содержимое в quoted-printable или base64. Декодированный текст остаётся тем же, а подписанные байты — нет.
Подписант хешировал другие окончания строк
иногдаКод подписи, который хеширует окончания строк LF, тогда как письмо отправляется с CRLF (или пробелы в конце строк позже удаляются), даёт хеш тела, который при канонизации `simple` никогда не совпадёт.
Как исправить
- 1.
Пересчитайте bh= самостоятельно
Сравнив хеш тела, пересчитанный для отправленной и для полученной копии, вы поймёте, кто виноват: подписант или ретранслятор.
bash # Recompute bh= for relaxed body canonicalization and compare with the header's bh= value python3 - message.eml <<'EOF' import base64, hashlib, re, sys raw = open(sys.argv[1], "rb").read().replace(b"\r\n", b"\n") body = raw.split(b"\n\n", 1)[1] lines = [re.sub(rb"[ \t]+", b" ", l).rstrip(b" \t") for l in body.split(b"\n")] canon = b"\r\n".join(lines).rstrip(b"\r\n") + b"\r\n" print("computed bh=", base64.b64encode(hashlib.sha256(canon).digest()).decode()) EOF grep -o "bh=[^;]*" message.eml - 2.
Подписывайте после всех изменений
Сделайте подписание DKIM последним шагом на пути исходящей почты; для посредников, которых вы не контролируете, по возможности полагайтесь на ARC и выравнивание DMARC через SPF.
text # Postfix: run content filters (disclaimers, antivirus) BEFORE the DKIM signing milter smtpd_milters = inet:127.0.0.1:10025, inet:127.0.0.1:8891 # ^ disclaimer milter ^ OpenDKIM (signs last)
Связанные ошибки
dkim=fail reason="signature verification failed"OpenDKIM (Authentication-Results header added by the opendkim milter); mailauth reports the same as "bad signature"dkim=permerrorOpenDKIM (Authentication-Results); mailauth reports syntax errors as dkim=neutraldkim=fail reason="key not found in DNS"OpenDKIM (Authentication-Results); mailauth reports it as dkim=neutral (no key)