2023-11-05
Exim's options for how to DKIM sign various email headers
Recently, I became aware that Exim has a relatively aggressive
list of message headers to sign with DKIM, and as a result we somewhat
reduced the list of headers to sign in our environment. As it
happens, Exim has several options for how it signs headers, which
are briefly covered in the documentation for the dkim_sign_headers
setting in (DKIM) Signing outgoing messages.
The effects of these options aren't really clear until we understand
the various meanings of DKIM signing message headers.
When you list a header name in the dkim_sign_headers
setting, you
can list it just by name or with either a '+' or an '=' character in
front of it. Listing a header with no prefix has the same meaning as
listing that header in the 'h=' list of the DKIM-Signature header; each
time you list it, it signs one instance of the header in the message. If
the header isn't there (or you've listed it more times than there are
copies in the message), you're oversigning, which prevents adding an
extra copy. So if dkim_sign_headers
includes 'from' once, you're
signing the first instance of From: in the message.
A prefix character of '=' means to sign as many copies of the header as are present in the message. If you specify '=from' and the message has two From: headers, you're signing against both. I believe it also means that if a particular header isn't present, you won't include it at all in the DKIM-Signature headers, so that it can be added later by another party without breaking the DKIM signature. If you want to sign things like the List-* family or the Reset-* family of headers if they're present but allow them to be added later, '=' would be one way.
(Since dkim_sign_headers
is subject to Exim string expansion,
another way is to only conditionally include headers based on whether
or not the message has them.)
Finally, a prefix character of '+' means to oversign the header; Exim will sign as many copies of the header as are present plus one extra, so that no new additional copies can be added later (without breaking the DKIM signature). In the common case this will generate two mentions of the header in DKIM-Signature:, because the header will only occur once in the message being signed. If you use '+' on headers that aren't present in the message, one mention of them will appear in DKIM-Signature, signing that they're not present.
Exim has no prefix character option to sign only the first instance of a header if it's present at all, although you can do that with conditional inclusion of a plain header name. In my view this wouldn't be a useful option; if multiple copies of a particular header are already present, you should either sign them all, which is covered by '=<header>', or reject the message as too suspicious (or at least not DKIM sign it, or perhaps have Exim remove the additional instances of the header).
Exim also has no prefix character option to oversign existing headers but not sign non-present headers at all (although you can do this with conditional inclusion of a '+' prefixed header name). This would let you seal List-* or Resent-* headers if they were present, but allow them to be added by someone else later if they weren't. I think this is a sensible thing to do with headers and DKIM signatures, but I could be wrong.
You can get the default value of dkim_sign_headers
with 'exim4
-bP macro _DKIM_SIGN_HEADERS
'; this (currently) uses the plain
header name format and lists each header once, meaning that Exim
by default signs the first instance of headers that are present and
the absence of headers that aren't present. There's also a
_DKIM_OVERSIGN_HEADERS
variant that puts a '+' in front of every
header name, which signs all present instances of each header name
and makes it so that no new ones can be added.
As a general thing, if you're presented with a message to sign that has multiple From:, Subject:, or whatever headers, I don't know if it's better to sign all of the instances of these headers, which might look suspicious to receivers, or sign only the DKIM first instance and let receivers assume the extra instances were added later, outside of your control. Possibly the best answer is to have Exim remove the extra instances.