Hi all,

I went through spec-06, bcp-01 and dns-00 looking for anything about header 
size limits in bytes and found nothing. BCP 7.5 covers the number of 
Message-Instance and DKIM2-Signature fields; I'm not trying to reopen the 
discussion about a ceiling on that number, but the size of those fields is the 
other half of the same problem and fits naturally in that section. Spec section 
16 is still TBA. The only place the topic shows up is section 4.3.1 of 
draft-moccia-dkim2-deployment-profile, which the WG decided not to adopt, so 
I'm not building on it, and I'm not proposing anything about the recipe or JSON 
design either. This is a small operator note for the BCP, with proposed text 
further down.

I think this deserves a few paragraphs because receivers fail in three 
different ways when the header section gets too big. Some reject during the 
SMTP transaction, which is fine because the sender finds out. Some drop the 
connection. Others truncate silently, and for us that is the bad case. Every 
DKIM2-Signature covers all Message-Instance and DKIM2-Signature fields, so a 
single truncated field breaks every signature on the message, and the verifier 
reports "Message-Instance m=x contains invalid JSON" against a Reviser whose 
message was well formed when it left.

I asked Claude to research what are the header limits defaults on different 
MTAs, message stores and cloud providers, details are below. "Total" is the 
whole header section, "per header" is one logical field.

- Sendmail 8.18: MaxHeadersLength 32768 total, 552 5.6.0 [1]
- Courier: BOFHHEADERLIMIT 100000 total (plus 500 headers and 5000 bytes per 
line), 534 [2]
- Exchange Server: MaxHeaderSize 256 KB total, connection closed [3]
- Exchange Online: 256 KB total and 32 KB per header, 552 5.3.4 [4]
- Gmail: 512000 bytes total, 5000 fields, 32 KB per value (To, Cc and 
References exempt from the per-value cap but counted in the total), 552 5.3.4 
[5]
- Cisco ESA: 1000 header lines per listener, 552 #5.3.4 [6]
- Exim: header_maxsize 1 MiB total, 552 [7]
- Maddy: max_header_size 1 MiB total, 552 5.3.4 [8]
- Postfix: header_size_limit 102400 per header, excess silently discarded, and 
capped at 60000 when milters are in use [9]
- Dovecot 2.4: 10 MiB per MIME part header block and 50 MiB per message, 
silently truncated [10]
- Cyrus and Haraka: 1000 header lines, the rest skipped without rejecting 
[11][12]

The smallest total, Sendmail's 32 KiB, sits right next to the 32 KB per-field 
limit at Gmail and Exchange Online. Postfix's cut happens on receipt, before 
any DKIM2 verifier sees the message.

For size, I asked Claude to do an estimation: a DKIM2-Signature with RSA-2048 
plus Ed25519 and one rt= recipient is about 620 bytes folded at 78 columns 
(about 510 with RSA only), and a Message-Instance with a sha256 pair and no 
recipe is about 125 bytes (about 310 with sha512 too). A 10 hop path that only 
forwards is 6 to 8 KB. The 31 and 63 hop ceilings floated in the 7.5 thread 
come to roughly 19 KB and 39 KB of signatures alone, so 63 hops of dual 
signatures already passes Sendmail's default before any other header is 
counted. The worst case for recipes is a "d" step that has to carry a removed 
base64 attachment: after the JSON and base64 layers the header is about 1.85 
times the decoded attachment size (1.92 after folding), which puts a single 
Message-Instance over 32 KiB at about 17 KB of attachment, over 102400 bytes at 
about 53 KB, over 256 KB at about 136 KB and over 500000 bytes at about 260 KB. 
These are Claude’s estimations for a literal copy, but I could do some real 
measurements if needed.

Here is the text I'd propose. Rather than adding a section, I'd retitle 7.5 and 
append to it:

7.5. Discussion re: Maximum Number and Size of Message-Instance and
DKIM2-Signature Header Fields

[existing text of 7.5 unchanged, then:]

Many MTAs, mailbox servers and hosted providers limit the size of the
header section of a message, the size of individual header fields, or
the number of header lines. Defaults vary widely: total header
section limits of 32 KiB (Sendmail), 100 KB (Courier), 256 KB
(Exchange) and about 500 KB (Gmail) are all in use, and some
implementations limit a single header field to 32 KB (Gmail, Exchange
Online) or 100 KiB (Postfix). Some of these limits are enforced by
rejecting the message during the SMTP transaction; others close the
connection, or truncate or discard the excess without any indication.

A Message-Instance or DKIM2-Signature header field that is truncated
in transit invalidates every DKIM2-Signature on the message, because
each signature covers all of those header fields (Section 9.6 of
[DKIM2]), and the failure is likely to be attributed to the wrong
party. Before adding its header fields, a Reviser SHOULD check that
the Message-Instance header field it adds, measured as transmitted
(field name and folding included), does not exceed 30000 octets, and
that the header section as a whole, once its Message-Instance and
DKIM2-Signature header fields are added, does not exceed 100000
octets. If a content-bearing Body Recipe would break either bound,
the Reviser SHOULD use a null Body Recipe (Section 5.2 of [DKIM2])
instead: a null Recipe is a visible, predictable outcome, whereas a
truncated one is not. Header Recipes cannot be null (Section 5.1 of
[DKIM2]); a Reviser whose header field changes would need a Header
Recipe that breaks either bound SHOULD NOT make those changes.

Operators who choose a ceiling on the number of Message-Instance and
DKIM2-Signature header fields SHOULD take the size of those fields
into account: a ceiling of N hops implies roughly N times the size of
one DKIM2-Signature header field in the header section. That size is
0.5 to 0.7 KB with two signatures and a single rt= value, and grows
with each additional recipient listed in rt=.

Operators who enforce header size limits on inbound mail SHOULD
reject messages that exceed them during the SMTP transaction (for
example with 552 5.3.4) instead of accepting and truncating. A total
header section limit below 100000 octets, such as the Sendmail
default of 32768, can reject legitimate DKIM2 mail with a long
forwarding path even when no Recipes are present, and operators of
such systems SHOULD raise it to at least that value. Beyond that,
operators SHOULD NOT raise header limits merely to accommodate DKIM2
header fields: unbounded header parsing has been a repeated source of
denial of service vulnerabilities in mail software.

One sentence at the end of 5.7:

The size of a content-bearing Recipe is also a practical concern; see
Section 7.5.

And one sentence at the end of the "Resource exhaustion via unbounded chains" 
item in section 8, since the DoS angle is real (Dovecot has shipped several 
fixes for header floods, for example CVE-2024-23185 for one very large header 
and CVE-2026-27852 for headers with huge numbers of addresses or parameters 
[13]):

The size of those header fields is a related concern; Section 7.5
recommends bounds on the header fields a Reviser adds and asks
receivers to reject oversized header sections rather than truncate
them.

The two numbers come from different limits. 30000 octets sits under the 32 KB 
per-field limit at Gmail and Exchange Online with room for folding; a bigger 
Message-Instance will be rejected by the two largest receivers whatever the BCP 
says, so there is little point in allowing more. 100000 octets is the smallest 
total among the common defaults once Sendmail is set aside. Sendmail's 32 KiB 
is too small for a long DKIM2 chain even without recipes, which is why the text 
asks those operators to raise it rather than asking Revisers to fit under it. 
Using the same figure on both sides means a Reviser that follows the text 
produces messages that a receiver following it will accept. I'm happy to change 
either number, or to soften the SHOULDs, if people would rather not have 
figures in the BCP.


Best,
Mauro


[1] 
https://github.com/freebsd/freebsd-src/blob/d39bd2c1388b520fcba9abed1932acacead60fba/contrib/sendmail/src/conf.h#L117-L118
 and 
https://github.com/freebsd/freebsd-src/blob/d39bd2c1388b520fcba9abed1932acacead60fba/contrib/sendmail/src/collect.c#L686-L702
[2] https://www.courier-mta.org/courier.html and 
https://github.com/svarshavchik/courier/blob/749aee4a79da5e100843a34968c841bd055141a9/courier/courier/submit.C#L1709-L1710
[3] https://learn.microsoft.com/en-us/exchange/mail-flow/message-size-limits
[4] 
https://learn.microsoft.com/en-us/office365/servicedescriptions/exchange-online-service-description/exchange-online-limits
[5] 
https://knowledge.workspace.google.com/admin/gmail/gmail-message-header-limits
[6] 
https://www.cisco.com/c/en/us/support/docs/security/email-security-appliance/118495-technote-esa-00.html
[7] 
https://www.exim.org/exim-html-current/doc/html/spec_html/ch-main_configuration.html
 and https://github.com/Exim/exim/blob/exim-4.99.1/src/src/config.h.defaults#L89
[8] https://maddy.email/reference/endpoints/smtp/ and 
https://github.com/foxcpp/maddy/blob/v0.9.5/internal/endpoint/smtp/session.go#L421-L424
[9] https://www.postfix.org/postconf.5.html#header_size_limit and 
https://github.com/vdukhovni/postfix/blob/v3.11.0/postfix/src/cleanup/cleanup_message.c#L1183-L1190
[10] 
https://github.com/dovecot/core/blob/2.4.5/src/lib-mail/message-header-parser.h#L5
 and 
https://github.com/dovecot/core/blob/2.4.5/src/lib-mail/message-parser.h#L22
[11] https://www.cyrusimap.org/imap/reference/manpages/configs/imapd.conf.html
[12] 
https://github.com/haraka/Haraka/blob/39846c5e4dd1f9da0a5f4ad050cfe21b9895ab72/config/connection.ini#L42
[13] 
https://documentation.open-xchange.com/dovecot/security/advisories/html/2024/oxdc-adv-2024-0003.html
 and 
https://documentation.open-xchange.com/dovecot/security/advisories/html/2026/oxdc-adv-2026-0003.html

_______________________________________________
Ietf-dkim mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to