Mystère DKIM & mail HTML : ça ne signe plus correctement à 997 chars et plus

Tags: #<Tag:0x00007f50a27524a8>

Hello,
J’ai un serveur de mail correctement configuré, avec plusieurs domaines, tout le bizarre, protégé par SPF/DMARC/DKIM.
Tous mes mails envoyés depuis des clients mails classiques, html ou text, tout est noté 10/10 à mail-tester.

J’envoie également des mails de façon authentifiée depuis d’autres serveurs, en bash, depuis mail ou mailx.

J’ai donc un petit script pour faire cela. Ce qui suit me permet d’envoyer un mail en HTML, avec toute la partie email compliant aux standards mails. Quand je teste ce qui suit avec mail-tester, 10/10.

mail_html () {
        debuthtml='<html><body style="background-color:#ececec;"><table>'
        message=$1
        finhtml='<p>test</p></table></body></html>'

        donnees="$debuthtml $message $finhtml"
        messageid=$(date |md5sum |awk '{ print $1 }')
        echo $donnees | /usr/bin/mail -s "$2" "$3"      -a "Message-ID: <${messageid}@mail.domain.org>"         \
                                                        -a "MIME-Version: 1.0"                                  \
                                                        -a "Content-Type: text/html; charset=UTF-8"             \
                                                        -a "Content-Transfer-Encoding: 8bit"                    \
                                                        -a "Content-Disposition: inline"                        \

        (($?)) && log "Erreur à l'envoi du mail : $3 pour $4 de la part de $2" ${DIRECTORY}/${ERROR_FILE}
}

Là où ça se complique, c’est lorsque j’essaye de faire un peu de mise en forme HTML, soit dans le contenu du body, soit même lorsque je veux par exemple ajouter les head. Là, ça signe, mais plus comme il faut.

En fait, si vous reprenez mon exemple, j’ai à un endroit

test

. Si je double, en faisant un

test

test

, ce qui n’a rien d’extravagant, là, la signature DKIM ne correspondra plus !

Je m’arrache les cheveux… j’ai l’impression que si je fais partir mon mail avec du code HTML crado (pas de doctype, head, tr/td, etc…), la signature DKIM se fait correctement, mais dès que je rajoute une bricole à la partie HTML de mon script, ça signe toujours, mais la signature n’est plus conforme ! Je ne comprends pas. C’est flagrant avec le test des balises

. Pourquoi il prend un paragraphe mais pas deux ??

De la même façon, si je reprends mon script, avec la méthode qui fonctionne à 100% et que j’essaye de rajouter un bête style à mon paragraphe (par exemple une couleur de texte), idem, DKIM ne signe plus correctement le mail… C’est incompréhensible.

C’est-à-dire, tu vois ça comment que ça ne signe plus correctement ?

Si c’est ton serveur e-mail qui s’occupe de la signature, je ne vois pas pourquoi ça ne fonctionnerait pas seulement en faisant la modification dont tu parles (bien que je ne comprenne pas trop de quoi il s’agit).

Je vois que la signature n’est plus bonne en analysant la source de mon mail arrivé à destination.

Dans le principe, la signature DKIM doit être la même à l’arrivée qu’au départ, ce qui garantit que le mail n’a pas été modifié entre son envoi et sa réception. Eh bien là, dans un cas, il apparaît modifié et pas dans l’autre.

Plus simplement, on peut aussi contrôler ça en faisant un envoi chez Gmail et en regardant le message original, gmail indique ce qui est PASS, et normalement, je devrais avoir SPF,DKIM et DMARC. Mais dans le cas simple, les 3 sont PASS, par contre quand je fais ma modif, DKIM est failed.

En faisant un test de façon plus empirique, en appelant mon script ainsi : mail_html « $( cat /dev/urandom | LC_ALL=C tr -dc ‹ a-zA-Z0-9 › | fold -w 259 | head -n 1) » « sujet » $1
==> tout est signé comme il faut.

Si je remplace 259 par 260, ça ne signe plus comme il faut. Y aurait-il un lien avec ce que mentionne dans son alinéas 2.1.1 la RFC 5322 concernant la taille des mails ?
Parceque curieusement, le body de mon mail qui passe a 996 chars, et celui qui ne passe pas en a 997. Mais la limite semble être fixée à 998 :face_with_monocle:

Je vois aussi que la source du mail est curieuse :

image

Vous voyez, à la fin du mail, la balise body n’est pas fermée correctement. Si j’envoie un mail avec 996 chars (et DKIM OK), là elle se ferme bien.
En fait, mon mail part, si il a plus de 997 chars, le body est modifié pour raison inconnue… et pour le coup, ça explique pourquoi DKIM me fait un FAIL.

La question qu’il faut donc se poser : pourquoi mailx/mail modifie le body de mon mail si celui-ci fait plus de 996 chars ?

Je viens de lancer mailx en mode verbose et m’aperçois que le mail semble partir correctement, du moins, avec le body inchangé :

> -->
> --> <table border="0" cellspacing="0" cellpadding="0"><tr style="border:none;"><td style="background-color:#ECECEC; color:#b30000;"><h1 style="text-align:center" >Message de test</h1></td></tr><tr style="border:none;"><td style="background-color:#CDCDCD;">Envoyé le 22/12/2022 15:25:43</td></tr><tr><td> fSeF9jfIxqVjK1HuiCP6J6vhwgq4L7HIlSy1LTzAlaMsA7ZtqBgTrJCt1mtCrLeN8O2vEkcLrS6lDvumFmxhF08FBT7ThHiV2VkCAoTM62X9EMYcF9kwA5ahqU0hubJ9jsjGxNYbSHg3pbmvHW3MJZTSSJgYNYOSa3HpYPar26C1VPAWU7XZhICm8BH6Dw41yZJ642kKuTNvXpnSmmcj0GZ5qqSaXmfjvHLNJWvgyiAoKEcmu1Y5Pz93roCCoZk5MxkrX3U7edGMuzTra3fuAmiRj2I9KIeTzMdFqxT2wmlKOCaECO9nleqX2NvblsiO3JMajQWNcxRv9v8keSHBGEBfmnurja8fFcemhHsmvLG2XhautbaNamQN2vk611yxjvL3xd2vtBrr62VgIjJEjQXepmL6M0JBskQALyywyzeMlX8ZZ0EOpKcJGz1k4WH </td></tr><tr><td style="background-color:#ECECEC"><p style="text-align:center;"><strong><span style="color:#808080;"><em>--- Ceci est un mail envoyé par notre serveur, merci de ne pas y répondre ---</em></span></strong></p></td></tr></table>
> --> .
> <-- 250 2.0.0 Ok: queued as 05F6B63673
> --> QUIT
> <-- 221 2.0.0 Bye

Quand il arrive sur ma boîte gmail par exemple, comme dans l’image plus avant, on voit bien que le dernier char est déplacé à travers un saut de ligne. Voici comment ça se présente dans la source du mail obtenu chez gmail (ou free, c’est pareil) :

image

On dirait donc que la modification du body n’intervient pas depuis le MUA, mais visiblement au niveau du MTA, sur notre serveur de messagerie (conf : Postfix + Clamav + RSPAMD)

Est-ce que tu peux arrêter de mettre le texte sous forme d’image ? Le texte est du texte, mets-le comme du texte.

Alors là, il va falloir que tu nous dises comment ton serveur e-mail est « correctement configuré ».
J’ai aussi le DKIM sur mon serveur, quand j’ai eu ce genre d’erreur, ça venait du fait que les e-mails venaient d’un sous-domaine qui n’avait pas sa signature configurée correctement, vérifie que l’adresse d’expédition n’est pas modifié entre tes tests.
Est-ce que tu peux faire deux tests, un qui fonctionne et un qui ne fonctionne pas (mais que tu voudrais qu’il fonctionne, bien sûr) vers une de tes adresses gmail ou free et poste ici (sous forme de texte car c’est un texte) la source complète des deux e-mails (en masquant l’adresse de destination, bien sûr) afin qu’il soit possible de voir de façon plus clair le problème.

« correctement configuré », je t’accorde que c’est tout à fait relatif. Tous mes autres envois, faits par exemple depuis des serveurs Nextcloud, ça signe aussi comme il faut. L’envoie depuis les clients mails ET webmails signent aussi comme il faut. Dans tous les cas, si je n’ai pas 9.6 à mailtester, c’est du 10/10 avec SPF/DMARC/DKIM, donc je me dis que ma conf n’est pas trop dégueu.

Mais là, dans le cas présent, bizarrement, ça ne passe pas.

L’adresse expéditrice est exactement la même.

voici ce que tu as demandé :

Je vois deux différences dans celui dont DKIM ne signe pas comme il faut :

  • En rajoutant le 997eme caractère, le dernier caractère du body de mon mail se prend un saut de ligne (la balise fermante du body se retrouve après un saut de ligne au lieu d’être collée au body
  • La raison pour laquelle DKIM ne signe pas comme il faut : body hash did not verify.

J’ai aussi fais un envoi en debug que tu trouveras ici. Et à l’envoi, depuis le serveur où je fais l’envoi (pas le MTA donc), j’a l’impression que mailx envoie correctement le mail, c’est à dire que je ne retrouve pas encore ce saut de ligne qui apparaît après son passage sur le MTA d’envoi.

Info complémentaire : j’ai poussé mon script directement sur mon serveur de mail et j’ai fait un envoi depuis celui-ci (donc ici je n’envoi plus depuis un serveur tiers à travers mon serveur de mail, je tente tout sur la même machine), le phénomène est exactement le même. Si je rajoute un caractère, c’est mort, la signature n’est plus bonne !

Je colle également ici un extrait de la RFC 5322 concernant Internet Message Format qui dit ceci :

There are two limits that this specification places on the number of
characters in a line. Each line of characters MUST be no more than
998 characters, and SHOULD be no more than 78 characters, excluding
the CRLF.

The 998 character limit is due to limitations in many implementations
that send, receive, or store IMF messages which simply cannot handle
more than 998 characters on a line. Receiving implementations would
do well to handle an arbitrarily large number of characters in a line
for robustness sake. However, there are so many implementations that
(in compliance with the transport requirements of [RFC5321]) do not
accept messages containing more than 1000 characters including the CR
and LF per line, it is important for implementations not to create
such messages.

The more conservative 78 character recommendation is to accommodate
the many implementations of user interfaces that display these
messages which may truncate, or disastrously wrap, the display of
more than 78 characters per line, in spite of the fact that such
implementations are non-conformant to the intent of this
specification (and that of [RFC5321] if they actually cause
information to be lost). Again, even though this limitation is put
on messages, it is incumbent upon implementations that display
messages to handle an arbitrarily large number of characters in a
line (certainly at least up to the 998 character limit) for the sake
of robustness.

Curieusement, mon problème se pose dès lors que j’envoie en mode authentifié avec un mail dont le body dépasse les 997 chars… y-aurait-il un lien ?

/edit: je viens de tomber là-dessus : Forcer dans les email le Content-Transfer-Encoding à Quoted-Printable - WEBDEV 28 - Forums développeurs - PC SOFT
Il semble que je me rapproche de la solution, l’histoire du problème de découpage des mails de +997 chars devient de plus en plus probable.

==> je viens de trouver… en wrappant mon $contenu avec des doubles quotes, je rentre à nouveau dans le clous (le découpage se fait à nouveau correctement) et du coup, je resigne DKIM comme il faut.
Donc la solution à tout ce bazar, c’était simplement de remplacer $contenu par « $contenu ».

Ah, je crois que je comprends ton problème.
J’ai déjà rencontré ce souci, je l’avais résolu, à l’époque, en encodant en base64 directement.
C’était un truc codé en bash et la sortie de la commande base64 était correctement formatée, je n’ai pas cherché plus loin.

C’est assez curieux, je rencontre un nouveau problème : Si j’envoie un mail HTML avec l’indentation classique, le mail part bien, peu importe sa taille.
Mais si j’envoie le même mail, avec le format HTML compressé de partout (en virant les espaces, sauts, etc…), ça refail au DKIM.
J’ai l’impression qu’envoyer du HTML au format compressé fait croire dans le body qu’on envoie une donnée qui tient sur une seule longue ligne, et du coup la structure du mail, après signature, s’en trouve modifiée.

Donc j’ai mon body qui est stocké dans mon script dans une variable, et selon si c’est au format compressé ça signe bien ou non. Une idée ? Comment as-tu fait exactement avec l’encodage en base64 (content-transfert-encoding: base64 ?) ? Car j’ai essayé, si le mail est bien lu sous gmail, Thunderbird ne sait pas l’interpréter

Ah ? Il fait quoi ? Il t’affiche le corps comme une pièce jointe ? Il te crache le base64 ?

il le crache en base 64. T’avais fait comment toi exactement ?

C’était un code artisanal en bash que j’ai conçu dans un cadre professionnel en 2011-2012 qui servait à spammer des professionnels, autant te dire que je n’avais aucune envie d’en conserver une copie.
C’était un script qui prenait un code en HTML créé par le service de communication avec des séquences de caractères à personnaliser, qui le remplaçait ces chaînes par les valeurs idoines (pour le suivi des ouvertures et le désabonnement) et qui passait directement le code qui en sortait dans la commande base64 pour obtenir des lignes de 76 caractères au maximum.

J’ai des e-mails formatés comme ça dans ma boîte e-mail, l’entête utilisé est

--boundary
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: base64

suivi d’une ligne vide, puis le contenu en base64.