sender check: a Sender: line is held to the rule From: is held to
test / test (push) Waiting to run

The owner's decision (2026-10-16) on the security review's F1: a Sender:
line - the sending agent's, shown beside From: as "on behalf of", and
signed since 2.8.0 - was neither held to the login nor, before, signed: an
application could write its own From: and Sender: ceo@bank.com under this
server's signature. Now, when a message has one, it must be one address
the application may send as, and at most one such line (RFC 5322 § 3.6);
From: is judged first.

The refusal is "550 5.7.1 Sender: <address> - <why>", a send-log row of
reason "sender" (store.ReasonSender), tagged "wrong Sender"; the message
page names the line. The log tailer recovers such a row from cleanup's line
as it does a From: refusal (fromRefusalRe reads either). Help text as in
the design commit d9d9af3; guide, architecture, security and CHANGELOG say
it, security.md keeps Resent-From: (signed, not judged) as an accepted
risk.

Tests: TestSenderLineHeldToTheSameRule (own address, a name, another
application's, a stranger's, two addresses, two lines, From: first), the
recovery of a Sender: refusal line, the tag, the message page; e2e
sender_check_holds_the_sender_line (own Sender: passes and is signed,
another domain's refused at DATA and logged).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-16 07:41:42 +03:00
co-authored by Claude Opus 5.5
parent d9d9af3932
commit 0cceb549cc
18 changed files with 268 additions and 53 deletions
+18 -23
View File
@@ -61,9 +61,10 @@ bounded only by `message_size_limit` (now 1 MiB, refused above it); the old
key tree stayed readable by group `selfpost` and went into every backup (now
the panel's alone and left out); a hostname with capitals deferred every
notification (the server's name is kept in lower case for DKIM); only `From`
was over-signed (now the shown headers too) - all fixed. `Sender:` is now
signed but not held to the login, which waits for the owner; that and what the
header cap leaves are under Accepted risks.
was over-signed (now the shown headers too) - all fixed. `Sender:`, which
the review found neither held nor signed (F1), is now both: signed, and held
to the login like `From:` (the owner's decision, 2026-10-16). What the header
cap leaves is under Accepted risks.
(2) **Accepted risks** —
deliberate departures beyond the mandatory, recorded so the decision is not
lost.
@@ -318,7 +319,10 @@ operator can act on it.
What this does not promise is what a display name looks like to a person:
a name of another sender in it passes, signed with the application's own
key - a forged display name is as old as mail, and no key of another's is
involved. Nor does it look at `Sender:` (Accepted risks). What the client
involved. Since `2.8.0` a `Sender:` line - shown beside `From:` as "on
behalf of" - is held to the same rule when there is one, and refused as
`550 5.7.1 Sender: ...`; `Resent-From:` is signed but not judged
(Accepted risks). What the client
wrote reaches the reply, the log and the send log only as printable ASCII,
cut, and a refused message is one row of the log whatever its recipient
count. Until `2.4.0` nothing held the header: OpenDKIM chose the key by it
@@ -715,25 +719,16 @@ deferred item from the roadmap.
signed with that domain's key. Whoever may export the domain has the key,
and its applications may send as `postmaster@<hostname>` by right. The
guide says so; a hostname that is nobody's sending domain avoids it.
- **`Sender:` is signed but not held to the login** (open: the owner's
decision). Since `2.8.0` the signer signs `Sender:` and `Resent-From:`
and over-signs `Sender:`, so neither can be added or changed after the
message leaves without breaking the signature; the sender check judges
`From:` alone (`senderSession.Header` keeps `From` and `Subject`). An
application of `mine.com` can write `From: billing@mine.com` and
`Sender: ceo@bank.com`: the check passes it, the signature is
`d=mine.com`, DMARC aligns on `From:` and passes, and a mail program that
shows `Sender:` beside `From:` - Outlook as "on behalf of", by the
review's account, not tried on this build - shows the other address. No
key of another's is involved, as with a display name; until `2.8.0` the
header was neither checked nor signed. The fix the security review of
2026-10-16 (F1) gives, and recommends: the check also reads `Sender:` and
`Resent-From:` and refuses one whose single address the application may
not send as, under the rule `From:` is held to - a few lines in
`internal/milter/sender.go` and their tests. It changes what is refused
(an application whose mail program writes a `Sender:` of another domain
today would be refused), hence the owner's decision; this item goes when
it is taken.
- **`Resent-From:` is signed but not held to the login.** Since `2.8.0`
the sender check holds `Sender:` to the rule `From:` is held to (the
owner's decision, 2026-10-16; security review F1), and the signer signs
both `Sender:` and `Resent-From:`. `Resent-From:` - written when a
message is passed on again (RFC 5322 § 3.6.6) - is not judged: an
application of `mine.com` could write `Resent-From: ceo@bank.com` beside
its own `From:`, signed `d=mine.com`. Few mail programs show it, and no
key of another's is involved, as with a display name. Holding it too is a
few lines in `internal/milter/sender.go` (`judge()`); the trigger to do it
is a mail program that shows it in place of `From:`.
- **A header section is bounded at 1 MiB by the signer, not by the milter
library.** The signer counts a message's headers as Postfix hands them
over and refuses a section of more than 1 MiB with `552 5.3.4`