Vos tableaux de bord sont verts. Votre file d’envoi est vide. Pourtant Gmail (ou Microsoft, ou la passerelle d’un partenaire qui pense toujours qu’on est en 2009)
indique : « DKIM body hash did not verify ». Le message arrive. Le destinataire peut le lire. Mais la signature échoue,
l’alignement DMARC s’effondre, et soudain votre graphique de délivrabilité ressemble à une pente de ski.
La partie la plus frustrante : « DKIM, c’est juste un en-tête, non ? » Non. DKIM est un pari selon lequel personne ne touchera au message entre la signature et la vérification.
Dans la réalité, tout le monde y touche—MTAs, passerelles de sécurité, archivages, signatures de bas de page, proxies « utiles », et ce fournisseur qui jure qu’il n’ajoute qu’un
pixel de suivi « dans les en-têtes ».
Ce que signifie réellement « body hash mismatch »
Une signature DKIM inclut une balise nommée bh= (body hash). Le signataire canonicalise le corps (selon l’en-tête DKIM),
le hache, encode ce hash en base64, et le stocke dans bh=. Le récepteur répète ce processus sur le corps du message reçu.
Si le hash calculé ne correspond pas à bh=, vous obtenez une incohérence du hash du corps.
C’est distinct d’une défaillance de la signature des en-têtes. Des modifications d’en-têtes peuvent aussi casser DKIM, mais une incohérence du hash du corps pointe directement vers :
les octets du corps—après canonicalisation—ne sont pas identiques à ceux que le signataire a vus.
La canonicalisation est la trappe
DKIM propose deux modes de canonicalisation pour les en-têtes et le corps : simple et relaxed. Beaucoup de déploiements utilisent
relaxed/relaxed parce que cela tolère certains changements d’espacement dans les en-têtes et certaines différences d’espacement dans le corps.
« Tolère certains » fait beaucoup de travail dans cette phrase.
- canonicalisation body simple : presque aucune tolérance. Tout changement d’octet (y compris les fins de ligne) le casse.
- canonicalisation body relaxed : ignore les blancs en fin de ligne, contracte les séries de WSP, normalise les fins de ligne, et ignore les lignes vides à la fin.
Mais même relaxed ne vous sauvera pas si quelque chose insère un pied de page, ré-encode quoted-printable, change une frontière MIME, ou « normalise »
le renvoi à la ligne à l’intérieur d’un bloc base64. DKIM tolère les blancs négligés, pas l’édition créative.
Un modèle mental pratique : le hash du corps DKIM est une somme de contrôle de « ce qu’un humain lit », plus beaucoup de choses qu’un humain ne voit jamais—la structure MIME,
l’encodage de transfert, les frontières, et le placement exact des CRLF.
Faits et histoire intéressants qui expliquent les bizarreries d’aujourd’hui
- DKIM n’est pas né parfait. Il est issu de deux propositions concurrentes (DomainKeys et Identified Internet Mail) qui ont été fusionnées pour éviter une guerre de normes.
- Les modes « relaxed » existent parce que les MTAs continuaient de reformater les mails « utilement ». Les standards n’assumaient pas qu’un flux d’octets impeccable survivrait à la réalité.
- CRLF n’est pas négociable en SMTP. Un nombre surprenant de systèmes laissent encore passer des LF seuls dans les pipelines, et la canonicalisation DKIM est là où ce crime est constaté.
- Quoted-printable est une zone à risque pour DKIM. Il est conçu pour être re-wrap, et différents composants peuvent renvoyer à des colonnes différentes.
- Certaines solutions de sécurité modifient volontairement les messages. Elles ajoutent des bannières, réécrivent des URL, injectent des disclaimers, ou détonnent des pièces jointes. DKIM ne « comprend » pas l’intention.
- ARC existe parce que le routage brise DKIM. Quand un forwarder modifie le mail, DKIM échoue ; ARC a été introduit pour préserver les résultats d’authentification à travers les intermédiaires.
- « l= » (longueur du corps) est à la fois une fonctionnalité et un piège. Il peut empêcher les ruptures en ignorant le contenu ajouté, mais il permet aussi à des attaquants d’ajouter du contenu qui n’est pas signé.
- Les listes de diffusion ont rendu l’adoption de DKIM pénible. Les listes qui ajoutent des pieds de page ou réécrivent les sujets invalident les signatures de manière routinière.
- DKIM s’applique par message, pas par connexion. Toute transformation en milieu de route peut produire des échecs par destinataire qui semblent aléatoires.
Playbook de diagnostic rapide (ordre de triage qui fait gagner des heures)
Quand vous voyez une incohérence du hash du corps, la tentation est de fixer DNS et clés. Ne le faites pas. Les clés et DNS cassent généralement la vérification complètement.
L’incohérence du hash du corps crie « le message a été modifié ». Voici l’ordre qui trouve le goulot d’étranglement rapidement.
1) Confirmez que c’est vraiment une incohérence du corps (pas une des en-têtes)
- Recherchez dans les logs du vérificateur des mentions explicites de
body hash did not verifyou de mismatchbh=. - Si vous ne voyez qu’un « bad signature », vous avez besoin de plus de visibilité : capturez le message et vérifiez localement.
2) Identifiez où le message a été signé et où il a été vérifié
- Trouvez l’en-tête
DKIM-Signature: il vous indiqued=(domaine),s=(sélecteur),c=(canonicalisation), et parfoisl=(longueur du corps). - Suivez les en-têtes
Received:: votre modification survient probablement entre deux sauts adjacents.
3) Comparez les corps « avant » et « après » la modification
- Obtenez le message tel que vu par le signataire (ou le plus proche possible : au MTA de signature). Obtenez le message tel que vu par le récepteur (ou à votre dernier saut sortant).
- Faites un diff avec des outils qui respectent CRLF et MIME.
4) Cherchez les modificateurs habituels dans la chaîne
- Filtres de contenu (Amavis, Rspamd, passerelles antivirus, DLP).
- Pieds de page et disclaimers outbound pour conformité de marque.
- Réécriture d’URL et proxies de click tracking.
- Proxies SMTP qui re-chunkent ou re-wrapent.
- Archivages / systèmes de journalisation qui réinjectent le mail.
5) Corrigez le processus, pas le symptôme
- Assurez-vous que la signature se fait après toutes les modifications.
- Ou cessez de modifier le mail signé, et signez au dernier saut responsable.
- Si vous devez modifier après signature, acceptez que DKIM échouera et utilisez ARC pour la confiance en aval (là où c’est approprié).
Les causes sournoises que personne ne vous dit (et comment prouver chacune)
1) Pieds de page et disclaimers ajoutés « tardivement »
Le classique. Le juridique veut un disclaimer. Le marketing veut une bannière. La sécurité veut un avertissement « Courriel externe ».
Si c’est injecté après la signature DKIM, le hash du corps sera incohérent. S’il est injecté avant la signature, tout va bien—jusqu’à ce qu’un autre système en ajoute un autre.
Comment le prouver : localisez le texte ajouté dans le corps final et confirmez qu’il n’était pas présent au saut de signature. L’indice est une ligne de pied de page
qui apparaît après la dernière frontière MIME ou uniquement dans la partie text/plain.
2) Re-wrapping quoted-printable
Quoted-printable (QP) encode les longues lignes avec des césures douces (= en fin de ligne). Certains gateways re-wrapent les lignes à une colonne différente,
ou « normalisent » le QP en convertissant les espaces/tabs ou en changeant la façon dont ils cassent les lignes longues.
DKIM signe les octets du corps encodé, pas le contenu décodé. Si l’encodage QP change, le hash change.
Vous pouvez avoir le même rendu textuel et échouer DKIM.
3) Changements de frontière MIME ou d’ordre des parties
MIME est un document structuré. Si une passerelle réordonne des parties, change des frontières, ou convertit un multipart/alternative en
une structure différente, DKIM casse. Cela peut arriver avec des filtres « nettoyer le HTML » ou des détonateurs d’attachement qui reconstrusent l’arbre MIME.
4) Changement d’encodage Content-Transfer-Encoding (8bit ↔ quoted-printable ↔ base64)
Certains MTAs dégradent 8bit en quoted-printable quand ils estiment que le prochain saut ne gère pas 8bit (ou si leur configuration est conservatrice).
D’autres font l’inverse : ils détectent du QP et « le nettoient ». Dans tous les cas, DKIM hash ce qu’il voit. Changer le CTE change les octets du corps.
5) Normalisation des fins de ligne et la guerre CRLF/LF
SMTP utilise CRLF. Mais vous rencontrerez encore des composants qui stockent les messages avec LF et les reconstituent plus tard. S’ils font cela incorrectement,
vous pouvez vous retrouver avec des fins de ligne mixtes ou des différences de canonicalisation qui échappent à votre regard.
Avec relaxed la plupart des problèmes de fins de ligne sont tolérés. Avec simple, vous jouez à pile ou face dans un datacenter.
6) Suppression « utile » des espaces blancs à l’intérieur des parties MIME
Certains filtres de contenu suppriment les espaces en fin de ligne, retirent des « lignes vides supplémentaires », ou normalisent les tabs/espaces. La canonicalisation relaxed ignore certains WSP en fin de ligne,
mais pas le reformatage arbitraire à travers le corps, et pas les changements dans les en-têtes MIME à l’intérieur des parties.
7) Proxies SMTP qui chunkent et cas limites de dot-stuffing
Normalement, le dot-stuffing est correct et sûr pour DKIM. Mais un proxy SMTP buggy peut mal gérer les lignes commençant par un point, ou convertir incorrectement les points en réinjection.
C’est rare, mais quand cela arrive, le diff est minuscule et la douleur énorme.
8) Signature de la mauvaise représentation (ordre des milters / pipeline de filtres)
Si vous signez avant qu’un filtre modifie le corps, DKIM échoue. Si vous signez après, ça passe. Beaucoup de stacks signent au mauvais endroit par accident :
un milter signe tôt, puis un autre milter ajoute un en-tête, ou un filtre de contenu post-queue réécrit le corps.
Ce mode d’échec apparaît comme « ça passe pour certains destinataires mais pas pour d’autres », car seules certaines routes rencontrent le composant modifiant.
9) Réécriture d’URL pour click tracking
La réécriture d’URL change le corps. Point final. Certains fournisseurs prétendent le faire « sans toucher DKIM ». Ce qu’ils veulent dire :
ils peuvent préserver la DKIM des en-têtes pour les messages qu’ils ne réécrivent pas, ou ils ajoutent leur propre signature DKIM après réécriture.
Mais si vous vous attendez à ce que votre DKIM survive, ne laissez personne réécrire le corps après que vous ayez signé.
10) Gestionnaires de listes et relais
Les listes de diffusion ajoutent des pieds de page, modifient des en-têtes, et parfois reformatent le contenu. Les forwarders peuvent ré-encoder le contenu ou ajouter des en-têtes list-unsubscribe.
DKIM casse ; DMARC casse ; puis les gens accusent le DNS.
11) La balise l= : des « correctifs » qui créent des trous de sécurité
Vous verrez des conseils : « Mettez simplement l= pour que l’ajout de pied de page n’ait pas d’importance. » Cela indique aux vérificateurs de hasher seulement les N premiers octets du corps.
Oui, cela peut empêcher les incohérences du hash quand du contenu est ajouté à la fin.
Cela permet aussi à un attaquant d’ajouter du contenu après la portion signée—du contenu qui paraît légitime parce que la signature DKIM passe toujours.
Beaucoup de récepteurs se méfient ou ignorent l= pour de bonnes raisons. Utilisez-le seulement si vous avez réfléchi au modèle de risque et que vous contrôlez tout le chemin.
12) Reconstitution de message par des systèmes de journaling/archivage
Certains archivages de conformité capturent le mail, puis le réinjectent vers d’autres systèmes (journaling, supervision, scans en aval).
S’ils reconstituent le message (même « sans perte »), de petites différences s’insinuent : pliage d’en-tête, régénération des frontières MIME, changements de CTE.
L’incohérence du hash du corps suit.
Une idée paraphrasée de Richard Cook (ingénierie de la fiabilité) : Le succès cache le risque ; les échecs révèlent le vrai système que vous exploitez réellement.
Les incohérences du hash du corps DKIM sont l’échec qui révèle votre véritable pipeline de courriel.
Blague n°1 : DKIM est comme un sceau anti-sabotage—super jusqu’à ce que votre propre équipe continue de « juste vérifier ce qu’il y a à l’intérieur ».
Tâches pratiques : commandes, sorties et décisions (12+)
Voici ce que vous faites à 02:10 quand un VP dit « nos factures vont dans les spams » et que vous ne pouvez pas vous permettre un débat philosophique.
Chaque tâche inclut une commande, une sortie d’exemple, ce que cela signifie, et la décision que vous prenez.
Task 1: Récupérer le message brut depuis la file Postfix (pour ne pas deviner)
cr0x@server:~$ sudo postcat -q 3F2A91C02E
*** ENVELOPE RECORDS ***
message_size: 48231 704 1 0
message_arrival_time: Sat Jan 3 01:12:09 2026
sender: billing@example.com
*** MESSAGE CONTENTS ***
Received: from app01 (app01.internal [10.0.12.34])
by mx-out01.example.com (Postfix) with ESMTP id 3F2A91C02E
for <user@recipient.tld>; Sat, 3 Jan 2026 01:12:08 +0000 (UTC)
DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=mail;
h=from:to:subject:date:mime-version:content-type;
bh=VQeN9v5kG7WJQm...snip...=;
b=Q0Vn...snip...
Content-Type: multipart/alternative; boundary="=_b4c1a7f1d1"
...snip...
Ce que cela signifie : Vous avez maintenant le corps exact tel que votre MTA sortant l’a vu. C’est votre « entrée signée ».
Décision : Sauvegardez-le dans un fichier et vérifiez DKIM localement. S’il vérifie ici mais échoue chez le destinataire, quelque chose change après ce saut.
Task 2: Sauvegarder le message dans un fichier pour des tests reproductibles
cr0x@server:~$ sudo postcat -q 3F2A91C02E > /tmp/msg.eml
cr0x@server:~$ ls -l /tmp/msg.eml
-rw-r--r-- 1 root root 48231 Jan 3 01:14 /tmp/msg.eml
Ce que cela signifie : Vous avez figé les preuves.
Décision : Utilisez ce même fichier pour tester OpenDKIM, parser le MIME, et calculer les hashes de corps canonicalisés de manière cohérente.
Task 3: Extraire l’en-tête DKIM-Signature et noter la canonicalisation
cr0x@server:~$ grep -n '^DKIM-Signature:' -A2 /tmp/msg.eml
8:DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.com; s=mail;
9- h=from:to:subject:date:mime-version:content-type;
10- bh=VQeN9v5kG7WJQm...snip...=; b=Q0Vn...snip...
Ce que cela signifie : La canonicalisation du corps est relaxed. Cela réduit les causes probables : ce n’est pas un simple problème de CRLF, c’est plus probablement des modifications de corps ou des réécritures d’encodage.
Décision : Concentrez-vous sur les filtres qui modifient le contenu, pas seulement l’espacement.
Task 4: Vérifier DKIM localement avec OpenDKIM (base)
cr0x@server:~$ opendkim-testmsg -d example.com -s mail -k /etc/opendkim/keys/example.com/mail.private < /tmp/msg.eml
opendkim-testmsg: signature ok
Ce que cela signifie : Le message capturé correspond à l’attente du signataire. Votre clé et la configuration de signature sont correctes.
Décision : Arrêtez de toucher au DNS. Commencez à chercher des modifications en aval du saut de signature.
Task 5: Inspecter les en-têtes Received pour identifier la fenêtre de modification
cr0x@server:~$ grep -n '^Received:' -n /tmp/msg.eml
1:Received: from app01 (app01.internal [10.0.12.34])
3:Received: by mx-out01.example.com (Postfix) with ESMTP id 3F2A91C02E
Ce que cela signifie : Dans cet exemple, la chaîne est courte. Dans de vrais incidents, vous verrez de nombreux sauts (relais internes, passerelles de sécurité, MTAs cloud).
Décision : Identifiez le dernier saut que vous contrôlez avant le destinataire. Capturez aussi là-bas pour diff.
Task 6: Capturer le trafic SMTP sur l’interface sortante (si nécessaire)
cr0x@server:~$ sudo tcpdump -i eth0 -s 0 -w /tmp/smtp.pcap 'tcp port 25 and host 203.0.113.55'
tcpdump: listening on eth0, link-type EN10MB (Ethernet), snapshot length 262144 bytes
^C
124 packets captured
Ce que cela signifie : Vous avez capturé les octets réels sur le fil vers le prochain saut. C’est coûteux et bruyant ; utilisez-le quand les logs ne suffisent pas.
Décision : Reconstruisez la portion DATA et comparez-la à votre .eml stocké. S’il y a une différence, quelque chose entre la file et le fil réécrit.
Task 7: Vérifier les filtres de contenu after-queue dans Postfix (coupable fréquent)
cr0x@server:~$ postconf | egrep 'content_filter|smtp_tls_policy_maps|receive_override_options'
content_filter = smtp-amavis:[127.0.0.1]:10024
receive_override_options = no_address_mappings
Ce que cela signifie : Un filtre de contenu est configuré. Amavis (ou similaire) ré-encode souvent le MIME, surtout autour des pièces jointes et du QP.
Décision : Assurez-vous que la signature DKIM se fait après ce filtre, ou configurez le filtre pour un minimum de réécriture.
Task 8: Vérifier le mode de signature/vérification OpenDKIM et le placement du milter
cr0x@server:~$ grep -E '^(Mode|Socket|Canonicalization|OversignHeaders|SignHeaders)' /etc/opendkim.conf
Mode sv
Socket inet:8891@localhost
Canonicalization relaxed/relaxed
OversignHeaders From
SignHeaders From,To,Subject,Date,Message-ID,MIME-Version,Content-Type
Ce que cela signifie : OpenDKIM signe et vérifie (sv) et utilise relaxed/relaxed. Oversigning From est une bonne pratique.
Décision : Confirmez que ce milter s’exécute à l’étape d’« injection sortante finale », pas à une étape interne antérieure.
Task 9: Identifier si une passerelle injecte des bannières/pieds de page
cr0x@server:~$ grep -nE 'External Email|DISCLAIMER|This message originated' /tmp/msg.eml
cr0x@server:~$ echo $?
1
Ce que cela signifie : Le message en file ne contient pas de textes injectés courants.
Décision : Si le destinataire voit une bannière, elle est ajoutée après votre capture de file—probablement à une passerelle de sécurité sortante ou côté destinataire.
Task 10: Vérifier les césures soft de quoted-printable et le comportement de wrapping
cr0x@server:~$ grep -n '^Content-Transfer-Encoding:' -n /tmp/msg.eml | head
22:Content-Transfer-Encoding: quoted-printable
cr0x@server:~$ sed -n '30,60p' /tmp/msg.eml | sed -n '1,10p'
Dear customer,=0D=0A=0D=0APlease remit payment within 30 days.=0D=0A=
If you have questions, reply to this email.=0D=0A
Ce que cela signifie : Le corps du message est encodé QP ; la présence de = en fin de ligne et de =0D=0A explicite le rend fragile.
Décision : Soupçonnez tout gateway qui décode/ré-encode le QP, ou tout système qui « normalise » la longueur des lignes.
Task 11: Détecter CRLF vs LF (ne faites pas confiance à votre éditeur)
cr0x@server:~$ python3 - < /tmp/msg.eml
import sys
data = sys.stdin.buffer.read()
print("CRLF count:", data.count(b"\r\n"))
print("LF count:", data.count(b"\n"))
print("Bare LF count:", data.count(b"\n") - data.count(b"\r\n"))
PY
CRLF count: 812
LF count: 812
Bare LF count: 0
Ce que cela signifie : Ce fichier est propre avec des CRLF. Si une capture en aval montre des LF nus, vous avez trouvé un vecteur de mutation du corps.
Décision : Si des LF nus apparaissent après un saut, corrigez le relais/proxy en faute ou assurez-vous que la signature DKIM se fait après celui-ci.
Task 12: Extraire et comparer les frontières MIME (les changements de boundary sont une preuve flagrante)
cr0x@server:~$ grep -n '^Content-Type: multipart' -n /tmp/msg.eml
15:Content-Type: multipart/alternative; boundary="=_b4c1a7f1d1"
cr0x@server:~$ grep -n '^--=_b4c1a7f1d1' /tmp/msg.eml | head
27:--=_b4c1a7f1d1
88:--=_b4c1a7f1d1--
Ce que cela signifie : Vous connaissez le jeton de frontière attendu. Si une copie en aval a une frontière différente, quelque chose a reconstruit le MIME.
Décision : Traitez les rebuilders MIME comme hostiles à DKIM. Soit déplacez la signature après eux, soit configurez-les pour ne pas re-sérialiser le contenu.
Task 13: Vérifier si le signataire a utilisé la risquée balise l=
cr0x@server:~$ grep -o ' l=[0-9]\+' -n /tmp/msg.eml
cr0x@server:~$ echo $?
1
Ce que cela signifie : Aucune limite de longueur du corps n’est utilisée. DKIM attend que l’intégralité du corps reste stable.
Décision : Bon pour la sécurité ; mauvais pour les pipelines avec pieds de page. Réparez le pipeline, pas la signature, sauf si vous acceptez explicitement le risque du l=.
Task 14: Comparer deux corps de message de deux sauts (diff qui respecte les octets)
cr0x@server:~$ python3 - << 'PY'
import email, sys
from email import policy
def body_bytes(path):
with open(path,'rb') as f:
msg = email.message_from_binary_file(f, policy=policy.default)
if msg.is_multipart():
# take full serialized body (post headers) for byte diff
raw = open(path,'rb').read()
return raw.split(b"\r\n\r\n",1)[1]
else:
raw = open(path,'rb').read()
return raw.split(b"\r\n\r\n",1)[1]
a = body_bytes("/tmp/msg.eml")
b = body_bytes("/tmp/msg-from-gateway.eml")
print("body lengths:", len(a), len(b))
print("first differing byte index:", next((i for i in range(min(len(a),len(b))) if a[i]!=b[i]), -1))
PY
body lengths: 41802 42110
first differing byte index: 21794
Ce que cela signifie : Le corps a changé entre les sauts ; vous avez même la position approximative à inspecter.
Décision : Ouvrez les deux fichiers autour de cet index, identifiez le contenu inséré/réécrit, et liez-le à une fonctionnalité spécifique de passerelle.
Blague n°2 : Le courrier électronique est le seul système où ajouter un pied de page sympathique peut être traité comme du sabotage—parce que c’en est.
Trois mini-récits d’entreprise depuis la tranchée
Mini-récit 1 : L’incident causé par une mauvaise hypothèse
Une entreprise de taille moyenne exploitait une stack Postfix sortante propre et ennuyeuse avec OpenDKIM. Leur politique DMARC était quarantine, et ils en étaient fiers.
Puis ils ont déployé un produit « safe links » sur le chemin sortant—commercialisé comme « transparent ».
La mauvaise hypothèse était simple : « Il ne touche que les liens en HTML ; DKIM est relaxed ; ce sera bon. » Le premier symptôme n’a pas été une panne dramatique.
Ce fut une fuite lente : des partenaires utilisant un DMARC strict ont commencé à rejeter des bons de commande. En interne, tout semblait livré. Des 250 OK partout.
La percée est venue de la comparaison entre le message en file et celui reçu par une boîte de test sur un domaine différent.
La partie HTML avait réécrit des liens, et la partie text/plain avait gagné un paramètre de tracking sur des URL nues. Les deux parties avaient changé, donc l’incohérence du hash du corps était garantie.
Le produit ré-encodait aussi le quoted-printable différemment, donc même des messages sans liens cassaient parfois.
La correction a exigé une décision inconfortable : soit signer après la réécriture (ce qui signifie que la passerelle de sécurité doit DKIM-signer en tant que domaine de l’organisation),
soit arrêter de réécrire le mail sortant entièrement. Ils ont choisi de signer après la réécriture en déplaçant la « signature DKIM finale » au dernier saut et en s’assurant que la passerelle disposait
des clés de sélecteur correctes. Ils ont aussi ajouté une surveillance des mutations de contenu inattendues en hachant le corps à plusieurs sauts.
Mini-récit 2 : L’optimisation qui s’est retournée contre eux
Une autre organisation voulait réduire la charge CPU sur leurs MTAs. Quelqu’un a remarqué que leur filtre de contenu décodait et ré-encodait tout,
même quand aucune menace n’était trouvée. L’« optimisation » a été d’activer une option qui « normaliserait » le MIME et consoliderait les encodages pour une meilleure compression.
Cela semblait brillant en micro-benchmark : moindre bande passante sortante, messages archivés plus petits, moins d’encodages étranges. Puis DKIM a commencé à échouer seulement
pour certaines classes de messages : factures d’une application, et réponses de support d’une autre. Parce que ces applis utilisaient des bibliothèques différentes, le MIME différait juste assez
pour déclencher le chemin de réécriture du filtre parfois.
Les échecs étaient intermittents par destinataire parce que toutes les routes sortantes ne passaient pas par le même cluster de filtres. Certains destinataires voyaient DKIM passer. D’autres voyaient
l’incohérence du hash du corps. L’organisation a passé une semaine à blâmer la propagation DNS et le « cache côté destinataire », parce que c’est l’histoire qu’on raconte quand on n’a pas de diff.
La correction a été de revenir sur le paramètre « normalize » et de changer l’architecture : scanner et réécrire le contenu avant la signature DKIM, et traiter le saut de signature comme sacré.
La charge CPU est revenue, mais la délivrabilité a cessé d’être un lancer de dés.
Mini-récit 3 : La pratique ennuyeuse mais correcte qui a sauvé la mise
Une entreprise réglementée avait une règle : « Pas de modification de mail après signature. Jamais. » C’était impopulaire. Les gens voulaient des bannières, des pieds de page spécifiques aux départements,
et des changements de branding de dernière minute. L’équipe mail disait non comme un loisir.
Ils autorisaient le filtrage de contenu, mais seulement dans un pipeline défini en amont de la signature. Leur relais sortant final faisait exactement trois choses :
appliquer la politique TLS, limiter le débit des clients abusifs, et signer DKIM. Pas d’injection de bannière. Pas de réécriture d’URL. Pas de réinjection d’archivage.
Lors d’un incident dans l’écosystème des partenaires—plusieurs destinataires renforçant DMARC et les règles de filtrage—cette organisation a vu presque aucune perturbation.
Tandis que d’autres s’affolaient pour expliquer des incohérences du hash du corps, eux pouvaient rapidement montrer que les octets de leurs messages restaient stables de la signature à la livraison.
La pratique ennuyeuse n’était pas glamour ; c’était de l’hygiène opérationnelle.
Le détail qui « a sauvé la mise » : ils avaient un point de capture standard au relais final qui stockait le message post-signature exact pendant 48 heures pour la comparaison forensique.
Lorsqu’un destinataire affirmait « votre DKIM échoue », ils pouvaient prouver si la passerelle entrante du destinataire avait modifié le message après réception,
ou si l’échec se situait dans leur propre chaîne. La plupart des différends se réglaient rapidement. Les victoires silencieuses restent des victoires.
Erreurs courantes : symptôme → cause racine → correction
1) Symptom: DKIM échoue seulement lors de l’envoi à certains domaines partenaires
Cause racine : Réécriture dépendante de la route (relais sortant différent, smart host, passerelle TLS, ou appliance DLP pour ces domaines).
Correction : Comparez le message capturé au saut de signature final vs le message observé après la passerelle spécifique à la route. Standardisez le routage ou déplacez la signature après le dernier modificateur.
2) Symptom: DKIM échoue uniquement pour les emails HTML, pas pour le texte brut
Cause racine : Sanitisation HTML, click tracking, ou injection de bannière dans la partie HTML modifiant une partie MIME.
Correction : Désactivez la réécriture HTML pour la sortie, ou assurez-vous que le système de réécriture signe DKIM au nom du domaine organisationnel après modification.
3) Symptom: L’incohérence du hash du corps apparaît après l’activation d’un nouvel antivirus ou produit DLP
Cause racine : Le produit re-sérialise le MIME ou change le Content-Transfer-Encoding même quand « aucune menace n’est trouvée ».
Correction : Configurez un mode « pass-through » qui préserve les octets, ou placez la signature DKIM après le produit, ou choisissez un produit qui supporte « pas de réécriture sauf si nécessaire ».
4) Symptom: DKIM échoue de façon intermittente pour le même modèle de message
Cause racine : Réécriture non déterministe (tokens boundary aléatoires, wrapping QP variable, ou différences de comportement par nœud).
Correction : Rendre la sérialisation déterministe : verrouiller les bibliothèques, désactiver la normalisation, et signer au dernier saut. Assurez-vous aussi que tous les nœuds ont des configs identiques.
5) Symptom: DKIM passe dans les tests internes mais échoue « en production »
Cause racine : Votre chemin de test contourne un composant sortant réel (connecteur cloud, service de journaling, relais partenaire, ou proxy sortant).
Correction : Testez en utilisant la même route sortante que les destinataires en production. Capturez à chaque saut et faites un diff.
6) Symptom: Les messages avec pièces jointes échouent DKIM plus que d’autres
Cause racine : Le scan/détonation des pièces jointes reconstruit le MIME ou change le wrapping base64.
Correction : Déplacez la signature après le traitement des pièces jointes, ou configurez le scan pour éviter la ré-encodage quand c’est possible.
7) Symptom: DKIM casse après activation de « ajouter disclaimer au mail sortant » sur un Exchange ou une passerelle
Cause racine : Le disclaimer est inséré après la signature, ou la signature a lieu sur un saut différent de celui que vous croyez.
Correction : Réordonnez les règles de transport pour que les disclaimers se produisent avant la signature DKIM. Si vous ne pouvez pas, acceptez que votre DKIM échouera et ré-architectez.
8) Symptom: L’échec DKIM mentionne « body canonicalization simple »
Cause racine : Vous utilisez c=simple/simple ou simple et quelque chose touche aux espaces/aux fins de ligne.
Correction : Utilisez relaxed/relaxed sauf si vous contrôlez chaque saut et pouvez prouver la stabilité octet-par-octet.
Listes de contrôle / plan étape par étape
Checklist A: Premiers 30 minutes d’un incident d’incohérence du hash du corps DKIM
- Obtenez un message en échec avec en-têtes complets et source brute (depuis le destinataire si possible).
- Récupérez le même message depuis votre file sortante ou vos logs autour du même horaire (postcat, copie journaling, ou point de capture).
- Vérifiez la copie en file localement avec les outils OpenDKIM ; enregistrez si elle passe.
- Extrait les paramètres DKIM :
d=,s=,c=, et sil=est présent. - Comparez les corps octet-par-octet et trouvez la première différence.
- Identifiez le saut où la modification est survenue en corrélant les en-têtes Received et les règles de routage.
- Décidez : supprimer le modificateur, déplacer la signature après lui, ou accepter l’échec et implémenter ARC/mécanisme de confiance alternatif.
Checklist B: Rendre votre pipeline sortant sûr pour DKIM (l’architecture ennuyeuse)
- Définissez un unique relais « last-mile » sortant dont le rôle est de signer et d’envoyer, pas d’« améliorer » le contenu.
- Placez le filtrage de contenu, la réécriture, les disclaimers et le branding en amont de ce relais.
- Assurez-vous que le relais last-mile n’exécute pas de filtres post-queue susceptibles de réécrire le contenu.
- Standardisez la canonicalisation :
relaxed/relaxedpour la plupart des organisations. - Gardez la génération MIME cohérente entre les applications (le choix de la bibliothèque compte).
- Déployez un point de capture au relais last-mile pour des diffs forensiques (rétention limitée dans le temps).
- Surveillez les échecs DKIM via des boucles de rétroaction des récepteurs et en échantillonnant des vérifications sur des copies sortantes.
Checklist C: Questions pour la revue de changement des fournisseurs et internes
- Ce composant réécrit-il des URL, du HTML, ou ajoute-t-il des bannières ?
- Décode-t-il et ré-encode-t-il le MIME ? Préserve-t-il le Content-Transfer-Encoding ?
- Reconstruit-il les frontières MIME ou modifie-t-il la structure multipart ?
- Où dans le flux mail la signature DKIM se produit-elle par rapport à ce changement ?
- Pouvons-nous prouver la stabilité octet-par-octet après signature avec un diff ?
- Y a-t-il un routage par destinataire qui pourrait provoquer un comportement incohérent ?
FAQ
Pourquoi DKIM échoue quand le contenu de l’email « a l’air identique » ?
Parce que DKIM signe des octets, pas le rendu de votre client mail. Changer le wrapping quoted-printable, les frontières MIME, ou l’encodage de transfert peut préserver l’apparence mais modifier les octets.
La canonicalisation relaxed suffit-elle pour prévenir les incohérences du hash du corps ?
Elle évite les échecs dus à des différences triviales d’espacement. Elle ne protège pas contre l’injection de contenu, la réécriture d’URL, la restructuration MIME, ou le ré-encodage.
Dois-je utiliser c=simple/simple pour une meilleure sécurité ?
Seulement si vous contrôlez tout le chemin et pouvez prouver qu’aucune modification n’interviendra. Sinon c’est de l’auto-sabotage : vous échangerez la pureté théorique contre des échecs concrets.
Puis-je « réparer » ça en faisant tourner les clés DKIM ou en changeant le DNS ?
Pas si l’erreur est une incohérence du hash du corps. Les problèmes de clés et DNS produisent généralement « pas de clé », « mauvaise clé », ou des erreurs de vérification de signature non liées à bh.
Votre problème est la mutation du message.
Et signer avec l= pour ignorer les pieds de page ajoutés ?
Cela peut réduire les cassures quand des pieds de page sont ajoutés, mais cela affaiblit l’intégrité : des attaquants peuvent ajouter du contenu en dehors de la portion signée.
Beaucoup de récepteurs traitent l= avec scepticisme. Réparez le pipeline d’abord.
Pourquoi le routage rompt-il DKIM si souvent ?
Les forwarders modifient fréquemment les messages (re-encoding, ajout d’en-têtes de liste, rewrapping), et DKIM ne survit pas aux changements. ARC a été introduit pour aider à préserver l’authentification à travers les sauts.
Pourquoi ça échoue seulement pour des messages avec pièces jointes ?
Les pièces jointes déclenchent des scans et des manipulations de contenu qui peuvent reconstruire le MIME ou changer le wrapping base64. Ce sont des changements de corps, et ils invalident bh.
Comment décider où signer DKIM dans un environnement complexe ?
Signez au dernier saut que vous contrôlez avant l’internet public—après toutes les modifications de contenu. Faites de ce saut un composant ennuyeux. Tout ce qui est « intelligent » se passe en amont.
Les destinataires peuvent-ils provoquer une incohérence du hash du corps de leur côté ?
Oui. Certaines passerelles entrantes modifient le mail (bannières, réécriture d’URL, détonation). Si vous pouvez prouver que vos octets sortants sont stables, l’échec peut se situer dans la chaîne entrante du récepteur.
DKIM échoue-t-il plus souvent avec UTF-8 ou les caractères internationaux ?
Pas intrinsèquement. Les problèmes viennent des conversions d’encodage (8bit/QP/base64) et des dégradations de transport déclenchées par du contenu non ASCII.
Conclusion : prochaines étapes qui réduisent vraiment les incidents
L’incohérence du hash du corps DKIM est rarement « un problème DKIM ». C’est votre pipeline de mail qui vous dit que quelque chose réécrit le contenu après la signature.
Cette réécriture peut être bien intentionnée (bannières de sécurité) ou silencieusement destructive (re-wrapping QP, reconstructions MIME). Dans tous les cas, la correction est architecturale.
- Choisissez un relais last-mile et faites-en une machine sanctifiée pour signer-et-envoyer. Pas de disclaimers. Pas de réécriture d’URL. Pas de normalisation MIME.
- Déplacez toutes les modifications en amont de la signature, et considérez tout composant post-signature comme coupable tant qu’un diff octet-par-octet ne prouve pas le contraire.
- Instrumentez pour prouver : stockez une copie à courte rétention du message post-signature au dernier saut pour régler les disputes avec des preuves, pas des impressions.
- Standardisez la canonicalisation (généralement relaxed/relaxed) et évitez
l=sauf si vous acceptez ses compromis de sécurité. - Quand un fournisseur dit « transparent », demandez « Est-ce que le corps brut change ? » S’il ne peut pas répondre, supposez que oui.
Vous n’avez pas besoin d’aimer l’email pour bien l’exploiter. Il suffit d’arrêter de laisser des boîtes aléatoires réécrire vos octets après que vous ayez promis au monde qu’ils ne le feraient pas.