Retour au blog

Délivrabilité

Étude de cas : DKIM « body hash did not verify », le pied de page qui faisait échouer DMARC

Factures en spam, DMARC en échec : comment un pied de page Exchange Online a cassé la signature DKIM d’un ERP. Diagnostic et correctifs.

10 novembre 2026 - 11 min

Un email signé DKIM par un ERP reçoit un pied de page ajouté par une règle de transport ; le corps ne correspond plus au hachage bh= et la vérification DKIM échoue.

Une entreprise industrielle envoie ses factures depuis un ERP en ligne. Les messages partent par Microsoft 365. Depuis la publication d’une politique DMARC p=quarantine, plusieurs clients signalent que les factures arrivent en spam, voire n’arrivent plus. Rien n’a changé dans l’ERP. Le domaine publie bien SPF, DKIM et DMARC.

Ce cas illustre l’un des échecs DKIM les plus fréquents et les plus mal compris : dkim=fail (body hash did not verify). Le message est modifié après avoir été signé. Nous suivons ici la démarche de diagnostic pas à pas, des rapports DMARC jusqu’au correctif.

Scénario reconstitué. Ce cas combine des situations courantes observées en audit. Les domaines, le tenant et le prestataire sont fictifs ; aucune donnée client n’est reprise.

Le point de départ : les symptômes

  • Les factures envoyées par l’ERP arrivent en spam chez plusieurs clients sous Gmail et Microsoft 365.
  • Les emails envoyés à la main depuis Outlook par les salariés arrivent normalement.
  • Le problème est apparu quelques jours après le passage de DMARC de p=none à p=quarantine.
  • Le service informatique a vérifié le DNS : les enregistrements SPF, DKIM et DMARC existent et sont syntaxiquement corrects.

Ce dernier point est un piège classique. Un DNS correct ne prouve pas que les messages réels passent DMARC. Seuls les en-têtes d’un message reçu et les rapports DMARC montrent le résultat effectif.

Étape 1 : lire les rapports DMARC

Les rapports agrégés reçus à l’adresse rua donnent une première piste. Pour ce flux, ils montrent des messages partis des IP de Microsoft 365, avec un résultat DMARC en échec :

<record>
  <row>
    <source_ip>40.107.0.0</source_ip>
    <count>212</count>
    <policy_evaluated><disposition>quarantine</disposition>
      <dkim>fail</dkim><spf>fail</spf></policy_evaluated>
  </row>
  <identifiers><header_from>example.fr</header_from></identifiers>
  <auth_results>
    <dkim><domain>example.fr</domain><result>fail</result></dkim>
    <dkim><domain>tenant.onmicrosoft.com</domain><result>pass</result></dkim>
    <spf><domain>bounces.erp-saas.net</domain><result>pass</result></spf>
  </auth_results>
</record>

Trois informations ressortent. La signature DKIM de example.fr échoue. Une seconde signature, au nom de tenant.onmicrosoft.com, réussit mais n’est pas alignée sur le domaine From. SPF réussit pour le domaine de rebond de l’ERP, lui aussi non aligné. Résultat : aucun mécanisme aligné ne passe, DMARC échoue. Notre guide pour lire un rapport DMARC XML détaille ces champs.

Étape 2 : examiner un message réel

Un client accepte de transférer une facture en pièce jointe (format .eml), et non en transfert classique, pour conserver les en-têtes d’origine. L’en-tête Authentication-Results confirme le diagnostic :

Authentication-Results: mx.example-client.com;
  dkim=fail (body hash did not verify) header.d=example.fr header.s=erp1;
  dkim=pass header.d=tenant.onmicrosoft.com header.s=selector1-tenant;
  spf=pass smtp.mailfrom=bounces.erp-saas.net;
  dmarc=fail action=quarantine header.from=example.fr

Le message d’erreur est précis : body hash did not verify. La clé publique a été trouvée et la signature des en-têtes est cohérente, mais le corps du message ne correspond plus à l’empreinte calculée au moment de la signature. Ce n’est donc ni un problème de DNS, ni un problème de clé.

Comprendre le mécanisme : ce que signe DKIM

Une signature DKIM contient plusieurs étiquettes :

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=example.fr; s=erp1;
  h=from:to:subject:date:message-id;
  bh=Q2xQZ3Vh…=; b=Hk8vT1…
  • d= et s= désignent le domaine signataire et le sélecteur : la clé publique se trouve en erp1._domainkey.example.fr.
  • h= liste les en-têtes signés.
  • bh= est l’empreinte du corps (body hash), calculée après normalisation.
  • b= est la signature elle-même, qui couvre les en-têtes listés et l’étiquette bh=.
  • c=relaxed/relaxed fixe la canonicalisation, c’est-à-dire la normalisation appliquée avant le calcul.

La canonicalisation relaxed tolère de petites variations : espaces en fin de ligne, suites d’espaces, lignes vides à la fin du corps. Elle ne tolère pas l’ajout d’un texte. Un seul paragraphe ajouté, et l’empreinte recalculée par le destinataire diffère de bh=.

Étape 3 : trouver qui modifie le message

On compare le message tel que l’ERP l’a généré (copie disponible dans son historique d’envoi) au message reçu. Une différence apparaît à la fin du corps : un pied de page légal (« Ce message et ses pièces jointes sont confidentiels… ») absent de la version de l’ERP.

Ce texte vient d’une règle de transport Exchange Online du type « Appliquer une clause d’exclusion de responsabilité », créée par le service juridique pour tous les messages sortants. L’ERP n’envoie pas directement sur Internet : il relaie ses messages par un connecteur entrant du tenant Microsoft 365. Ces messages traversent donc les règles de transport, comme ceux des salariés.

On peut confirmer localement avec l’outil dkimverify de la bibliothèque Python dkimpy :

# message tel que reçu par le client
dkimverify < facture-recue.eml
# → signature verification failed

# message tel que généré par l’ERP (copie d’historique)
dkimverify < facture-erp.eml
# → signature ok
Schéma du cas : l’ERP signe DKIM, Exchange Online ajoute un pied de page et signe avec onmicrosoft.com ; chez le destinataire, DKIM échoue, SPF n’est pas aligné et DMARC échoue.
Le parcours du message : la signature de l’ERP est cassée par le pied de page, la signature Microsoft n’est pas alignée, SPF non plus. DMARC échoue.

Pourquoi les emails des salariés passaient

Les emails envoyés depuis Outlook reçoivent le même pied de page, mais ils ne sont pas signés avant son ajout. Exchange Online signe en fin de parcours, après les règles de transport. Leur signature est donc valide.

Le problème vient d’ailleurs : DKIM n’était pas activé pour le domaine personnalisé dans Microsoft 365. Exchange Online signait avec le domaine par défaut tenant.onmicrosoft.com, valide mais non aligné. Pour les salariés, c’est SPF qui sauvait DMARC : leur Return-Path appartient à example.fr, autorisé dans l’enregistrement SPF. Pour l’ERP, le Return-Path appartient au prestataire. Une fois la signature de l’ERP cassée, il ne restait aucun mécanisme aligné.

Tant que DMARC restait en p=none, cet échec était invisible pour les destinataires. Le passage à quarantine l’a rendu effectif. C’est exactement ce que la phase d’observation sert à détecter : voir notre méthode pour durcir DMARC sans casser ses emails.

Les correctifs, par ordre de priorité

1. Activer DKIM pour le domaine dans Microsoft 365

C’est le correctif de fond. Une fois DKIM activé pour example.fr dans le portail Microsoft Defender, Exchange Online ajoute en fin de parcours une signature alignée et valide, même si celle de l’ERP est cassée. DMARC passe, car il suffit d’une signature DKIM alignée et valide.

# Exchange Online PowerShell
# créer la configuration si elle n’existe pas encore (sans l’activer)
New-DkimSigningConfig -DomainName example.fr -Enabled $false
# relever les deux CNAME à publier dans la zone DNS
Get-DkimSigningConfig -Identity example.fr | Format-List Selector1CNAME,Selector2CNAME
# une fois les CNAME publiés :
Set-DkimSigningConfig -Identity example.fr -Enabled $true

Publiez dans votre zone DNS les deux enregistrements CNAME indiqués par Microsoft, puis activez la signature. Ce correctif protège aussi les emails des salariés, qui ne dépendront plus du seul SPF.

2. Exclure le flux de l’ERP de la règle de pied de page

Ajoutez une exception à la règle de transport, par exemple sur l’adresse d’expédition de l’ERP ou sur un en-tête qu’il ajoute. La signature d’origine reste alors intacte. Si le pied de page est juridiquement nécessaire, faites-le figurer dans le modèle de facture de l’ERP, avant la signature.

3. Aligner SPF pour le flux de l’ERP, si le prestataire le permet

Certains prestataires proposent un domaine de rebond personnalisé (par exemple bounces.example.fr). SPF devient alors aligné, ce qui ajoute une seconde voie de réussite à DMARC.

À éviter : l’étiquette l=

La norme DKIM prévoit une étiquette l=, qui limite la signature aux premiers octets du corps. Elle rendrait la signature insensible au pied de page ajouté, mais elle permet aussi à un tiers d’ajouter du contenu à un message signé. Ce n’est pas une correction acceptable.

Étape 4 : vérifier le correctif

  1. Envoyer une facture de test vers une adresse Gmail et une adresse Microsoft 365, puis lire les en-têtes : on doit obtenir dkim=pass header.d=example.fr et dmarc=pass.
  2. Suivre les rapports DMARC pendant une à deux semaines : la source Microsoft 365 pour ce flux doit passer en pass.
  3. Documenter la règle : tout nouveau système qui relaie par Microsoft 365 doit être testé de la même façon.

Les leçons à retenir

  • Toute modification après signature casse DKIM : pied de page, bannière « expéditeur externe », réécriture des liens par une passerelle, ajout de mentions par un antivirus, listes de diffusion.
  • Signez en dernier. Le dernier système avant Internet doit signer avec votre domaine.
  • Un domaine Microsoft 365 sans DKIM personnalisé repose sur SPF seul, une base fragile dès qu’un relais ou un transfert intervient. Voir aussi le rôle de SRS dans les transferts et, depuis novembre 2026, les rejets Outlook.com des emails non authentifiés.
  • La phase p=none sert à voir ces cas. Ne la raccourcissez pas, et lisez les rapports avant de durcir.

FAQ

Que signifie « body hash did not verify » ?

Le corps du message reçu ne correspond pas à l’empreinte bh= calculée par l’expéditeur au moment de la signature. Le message a été modifié en transit, ou la signature a été calculée sur un contenu différent.

Un DNS correct peut-il produire cette erreur ?

Oui. Cette erreur ne concerne pas la clé publique : elle apparaît même quand l’enregistrement DKIM est parfait. Elle désigne un contenu modifié.

Pourquoi DMARC échoue-t-il alors qu’une signature DKIM passe ?

Parce que DMARC exige une signature valide et alignée sur le domaine From. Une signature valide au nom de onmicrosoft.com ne compte pas pour example.fr.

ARC peut-il résoudre ce problème ?

Non dans ce cas. ARC aide un destinataire à faire confiance à des intermédiaires qui modifient des messages, comme les listes de diffusion. Il ne remplace pas une signature alignée pour votre propre flux.

Les pieds de page ajoutés par un outil de signature posent-ils le même problème ?

Oui, si l’outil intervient après une première signature DKIM. Vérifiez où se fait la signature finale dans la chaîne et testez chaque flux concerné.

Conclusion

L’erreur « body hash did not verify » n’est presque jamais un problème de DNS : c’est un problème de chaîne de traitement. Le diagnostic tient en trois questions : qui signe, qui modifie après, et quel mécanisme reste aligné. Si vos flux traversent plusieurs systèmes (ERP, passerelle, Microsoft 365, outils de signature), notre prestation d’audit et de configuration SPF, DKIM et DMARC reconstitue cette chaîne avant de durcir votre politique.

Sources et références

Ce sujet touche vos propres envois ?

Décrivez ce que vous observez : je vous indique les premières vérifications utiles et la prestation adaptée.