Spoofed Email That Passes Every Check: Detecting What SPF, DKIM, and DMARC Miss
The uncomfortable truth about modern email spoofing is that authentication isn't failing, it's passing, exactly as designed. The dangerous messages arrive with valid SPF, a good DKIM signature, and DMARC alignment, because the attacker owns the domain they're sending from and configured it correctly. Andrew Kowal's LinuxSecurity piece lays out why human red-flag training and protocol checks both miss this, and it's right. What it's light on is the Linux side it promises, so this rebuild gives you the actual Postfix, rspamd, and OpenDKIM checks to detect what authentication structurally cannot, plus a command to find the invisible-Unicode tricks that are the current growth area.
Why Authentication Passing Is the Problem, Not the Solution
SPF, DKIM, and DMARC verify that a message came from a server authorized to send for a domain and wasn't tampered with in transit. They say nothing about whether the domain or the human behind it is trustworthy. A modern attacker doesn't forge your domain and get bounced; they register example-billing.com, configure perfect authentication, warm the domain for weeks with legitimate traffic, and then send a well-authenticated message that has no reputation problem yet. Or they skip all that and send from a genuinely compromised mailbox at a real business your recipient trusts, which passes every check because it is legitimate mail infrastructure, just in the wrong hands.
The gateway isn't the target. One human is. That reframes the entire defense: you're not tuning a filter to reject bad auth, you're building signals that flag authenticated mail whose content or structure doesn't add up.
The Structural Gaps in SPF, DKIM, DMARC
Precise about where each one stops, because the gaps are where detection has to live:
- SPF only checks the envelope sender (
MAIL FROM), not theFrom:header the human reads. A message can pass SPF for one domain while displaying an entirely differentFrom:address. The reader never sees the envelope. - DKIM signs specific headers and the body. Add unsigned headers, or manipulate content outside the signed portion, and the signature still validates. DKIM proves some headers weren't altered, not that the visible message is honest.
- DMARC ties From-header alignment to SPF/DKIM, but only if you enforce it. A
p=nonepolicy reports without blocking. If your DMARC is stuck at monitoring because enforcement once broke a newsletter or a ticketing integration, it stops nothing.
⚠️ Check your own domain's DMARC enforcement right now, because "we have DMARC" and "we enforce DMARC" are different claims:
dig +short TXT _dmarc.yourdomain.com
If that returns p=none, you're in report-only mode and exact-domain spoofing of your domain is not being blocked, only logged. Moving to p=quarantine then p=reject after you've confirmed legitimate senders pass is the single highest-value protocol change most operators are sitting on. Everything below is for the spoofing DMARC can't catch even at p=reject: lookalike domains and compromised real mailboxes.
Detecting Display-Name and Reply-To Tricks in Postfix
Two of the most common header tricks are invisible to authentication because they're rendering choices, not protocol violations:
- Display-name spoofing. The friendly name shown next to the address is set to "Payroll" or your CFO's actual name, while the real address hides in the truncated mobile view.
- Reply-To redirects. The
From:looks clean, butReply-To:points elsewhere, so the reply starts a conversation with the attacker.
Neither trips SPF or DMARC. But you can score them at the MTA. A Postfix header_checks rule to flag a Reply-To on a different domain than From for your own inbound is crude but catches the lazy version:
/etc/postfix/main.cf:
header_checks = regexp:/etc/postfix/header_checks
The real work belongs in rspamd, which already parses both headers structurally. rspamd ships a FROM_NEQ_ENVFROM symbol (From header not matching envelope sender) and reply-to checks; the useful move is raising their weight for mail claiming to be internal. In /etc/rspamd/local.d/, a multimap that boosts the score when the display name matches an executive but the domain isn't yours turns a rendering trick into a measurable score bump:
# /etc/rspamd/local.d/multimap.conf
EXEC_IMPERSONATION {
type = "header";
header = "From";
map = "/etc/rspamd/maps/exec_names.map";
regexp = true;
score = 6.0;
description = "Display name matches an executive on a non-corp domain";
}
⚠️ Pair that with a domain allowlist so it only fires when the executive name appears off your legitimate domains, or you'll score your own CEO's real mail. The value isn't blocking (false positives are certain), it's adding weight so a display-name impersonation carrying a payment request crosses the quarantine threshold it would otherwise slip under.
The Invisible Unicode Angle: Detect It, Don't Just Fear It
The growth area in spoofing is characters that render as nothing: zero-width spaces (U+200B), zero-width joiners (U+200D), soft hyphens (U+00AD), and bidirectional overrides. They display as empty pixels but change how filters parse a string, a URL split by a zero-width joiner can dodge a reputation check while rendering as one clean link, and a name broken with soft hyphens slips past name-matching rules.
The defensive answer is to detect their presence, because legitimate business email essentially never contains zero-width characters in headers or visible text. Their presence alone is a strong signal. Scan a raw message for the common offenders:
grep -aP '[\x{200B}\x{200C}\x{200D}\x{FEFF}\x{00AD}\x{202A}-\x{202E}\x{2066}-\x{2069}]' /path/to/message.eml && echo "ZERO-WIDTH / BIDI CHARS PRESENT"
To see exactly which code points and where, so you can tell a spoofing attempt from a legitimate quirk:
perl -CSD -ne 'while(/([\x{200B}\x{200C}\x{200D}\x{FEFF}\x{00AD}\x{202A}-\x{202E}\x{2066}-\x{2069}])/g){printf "U+%04X at offset %d\n", ord($1), pos()}' /path/to/message.eml
⚠️ For your live mail flow, wire this into rspamd rather than running it by hand. rspamd's regexp module can match invisible code points in headers and subject, and a custom rule scoring their presence in the From, Subject, or visible body catches the class without you inspecting messages individually. The principle: treat zero-width and bidi-override characters in human-readable fields as guilty until proven innocent, because their legitimate use in email is close to nonexistent.
The DKIM Key Permission Check Everyone Forgets
Authentication protects you only if the private key stays private. ⚠️ A DKIM signing key in a world-readable file on a shared or multi-tenant host lets anyone who can read it sign mail as you and pass authentication downstream. This is the self-inflicted version of the whole problem, and it's a one-command check:
find /etc/opendkim /etc/dkimkeys /var/db/dkim -name '*.private' -o -name '*.key' 2>/dev/null | xargs -r ls -l
Any signing key that isn't 600 (or 640 owned by the signing user/group) is a finding. The key should be readable only by the OpenDKIM or rspamd signing process, nothing else:
chmod 600 /etc/opendkim/keys/yourdomain/default.private && chown opendkim:opendkim /etc/opendkim/keys/yourdomain/default.private
On a cPanel or multi-tenant box especially, audit this across every domain, one leaked signing key undoes all three protocols at once. This is the same key-hygiene discipline that matters for self-hosting mail properly, where the DKIM key is exactly the thing you back up separately and lock down hardest.
Test Your Own Flow Before an Attacker Does
The most useful habit the source recommends, and the one worth keeping: send crafted test messages through your own mail flow and observe what each layer reports. Not to build attacks, but to find your blind spots. Send yourself a message with a mismatched Reply-To, one with a display name matching a colleague on an external domain, and one containing a zero-width character in the subject, then check what rspamd scored, what the gateway logged, and what actually rendered in each client (desktop, mobile, webmail render these very differently).
The gap between "what my filter reported" and "what my phone displayed" is where real attacks live. This is the same controlled-testing discipline as validating a spam pipeline, and it pairs with feeding the results into a scoring layer like the local-LLM classification setup, which can weigh intent (a payment redirect, an urgent out-of-band request) that protocol checks ignore entirely.
Layer the Defense
No single control catches authenticated spoofing. Three layers together do meaningful work:
- Protocol hygiene enforced end to end. DMARC at
p=reject, notp=none. DKIM keys locked to600. SPF that actually reflects your senders. BIMI where the ecosystem supports it. - Content and behavioral scoring. Score what the message asks for, not just how it authenticates. Payment redirects, wire requests, and out-of-band contact requests should raise the score regardless of clean headers. This is where an LLM-assisted classifier earns its place, since it reads intent.
- Platform hardening below the protocol layer. Your Postfix and Dovecot config, your rspamd weights, and your header_checks shape what gets through. Misconfigurations here quietly widen exposure, and they're exactly what the controlled testing above surfaces. Off-host logging of your mail decisions, as in tamper-resistant logging, means you can reconstruct what a spoofed message did after the fact.
Bottom Line
A well-crafted spoofed email doesn't fail authentication, that's the entire point, so a defense built only on SPF, DKIM, and DMARC is defending against last decade's attack. Enforce those to p=reject and lock your DKIM keys to 600 first, because those close the self-inflicted gaps. Then build the detection authentication can't provide: score display-name and reply-to mismatches in rspamd, flag zero-width and bidi characters in human-readable fields as near-automatic signals, and test your own flow with crafted messages to find the gap between what your filter logs and what your users' phones actually show. The messages worth catching are the ones that pass every check, which means your job starts where authentication ends.