The DMARC OR Trap: How Attackers Bypass DKIM Without Breaking a Key
I saw a post from Karl (@supersat) on an email security-related X thread that took me back to something I learned while working in the cyber team of a critical infrastructure organisation back in 2019.
I can see how they could bypass SPF, but how do they bypass DKIM?
— karl (@supersat) 2026
It’s a fair question. DKIM uses cryptographic signatures. You can’t forge a private key. So how do attackers get past it?
They don’t. They make the receiving server stop asking for it.
DMARC uses OR logic
Back in 2019, I assumed that DMARC requires both SPF and DKIM to pass. It doesn’t. RFC 7489 defines DMARC authentication as a simple OR:
- SPF passes and aligns with the From domain, or
- DKIM passes and aligns with the From domain
If either path succeeds, DMARC passes. An attacker who can satisfy the SPF path never needs to touch DKIM at all.
graph TD
Start[Inbound Email] --> SPF_Check{SPF Path}
Start --> DKIM_Check{DKIM Path}
subgraph SPF ["The SMTP Envelope (SPF)"]
SPF_Check --> SPF_Auth[SPF Authenticated?]
SPF_Auth -- "Pass" --> SPF_Align[SPF Aligned?]
SPF_Align -- "Yes" --> SPF_Result[SPF VALIDATED]
SPF_Auth -- "Fail" --> SPF_Fail[SPF INVALID]
SPF_Align -- "No" --> SPF_Fail
end
subgraph DKIM ["The Email Header (DKIM)"]
DKIM_Check --> DKIM_Auth[DKIM Authenticated?]
DKIM_Auth -- "Pass" --> DKIM_Align[DKIM Aligned?]
DKIM_Align -- "Yes" --> DKIM_Result[DKIM VALIDATED]
DKIM_Auth -- "Fail" --> DKIM_Fail[DKIM INVALID]
DKIM_Align -- "No" --> DKIM_Fail
end
SPF_Result --> Decision{OR Gate}
DKIM_Result --> Decision
SPF_Fail --> Decision
DKIM_Fail --> Decision
Decision -- "At least one validated" --> Pass[DMARC PASS]
Decision -- "Both invalid" --> Fail[DMARC FAIL]
style Start fill:#e2e3e5,stroke:#6c757d,color:#212529
style SPF_Check fill:#cce5ff,stroke:#004085,color:#212529
style DKIM_Check fill:#cce5ff,stroke:#004085,color:#212529
style SPF_Auth fill:#d1ecf1,stroke:#0c5460,color:#212529
style SPF_Align fill:#d1ecf1,stroke:#0c5460,color:#212529
style SPF_Result fill:#d4edda,stroke:#28a745,color:#212529
style SPF_Fail fill:#f8d7da,stroke:#dc3545,color:#212529
style DKIM_Auth fill:#d1ecf1,stroke:#0c5460,color:#212529
style DKIM_Align fill:#d1ecf1,stroke:#0c5460,color:#212529
style DKIM_Result fill:#d4edda,stroke:#28a745,color:#212529
style DKIM_Fail fill:#f8d7da,stroke:#dc3545,color:#212529
style Decision fill:#fff3cd,stroke:#ffc107,color:#212529
style Pass fill:#d4edda,stroke:#28a745,color:#212529
style Fail fill:#f8d7da,stroke:#dc3545,color:#212529
If an attacker forces a pass on the left side (SPF), the OR gate delivers a DMARC pass regardless of what happens on the right side (DKIM).
How attackers exploit this
Two common misconfigurations make the SPF path easy to abuse.
1. Relaxed alignment is the default
Under relaxed alignment (aspf=r), DMARC considers a subdomain match good enough. If your visible From address is company.com, an SPF check from anything.company.com counts as aligned.
Subdomains do inherit the parent’s p=reject policy by default. The problem is that relaxed alignment acts as a bridge: an attacker who can pass SPF on any subdomain gets that result counted as aligned for the root domain’s From header. The p=reject policy on the root still passes because the SPF path succeeded with a valid alignment match. I’ve seen this trip up organisations that assumed their p=reject had them covered, not realising that relaxed alignment was letting subdomain SPF passes satisfy the root domain’s DMARC check.
2. Overly broad SPF includes
The other common problem is putting third-party include: statements on your root domain’s SPF record. When you add include:salesforce.com to your root SPF, you’re authorising every IP address that Salesforce uses to send email. An attacker with their own Salesforce account sends through that same infrastructure, and your SPF record says those IPs are legitimate senders for your domain. SPF passes, alignment succeeds, DMARC passes. No DKIM required.
The M3AAWG covers this in their SPF best practices: broad third-party includes on root domains create shared infrastructure risk that attackers can exploit. The same document also warns against wildcard SPF records (* TXT "v=spf1..."), which are rarer but effectively authorise every possible subdomain as a sending source.
Hardening your DNS
The fix is moving from a permissive configuration to least-privilege.
The vulnerable setup
| Record | Value | Risk |
|---|---|---|
| SPF (root) | v=spf1 include:salesforce.com ~all |
Any Salesforce customer can send as your root domain |
| DMARC | v=DMARC1; p=reject; |
Defaults to relaxed alignment with no subdomain protection |
The hardened setup
| Record | Value | Benefit |
|---|---|---|
| SPF (root) | v=spf1 mx ~all |
Only your MX hosts can send, softfail defers enforcement to DMARC |
| SPF (mktg subdomain) | v=spf1 include:salesforce.com ~all |
Third-party sending risk isolated to one subdomain |
| DMARC | v=DMARC1; p=reject; sp=reject; aspf=s; adkim=s; |
Strict alignment, explicit subdomain rejection |
You’ll notice the hardened records use ~all (softfail) rather than -all (hardfail). This sounds counter-intuitive on a domain with p=reject, but there’s a good reason. A hardfail tells the receiving server to reject the message on SPF alone, before it ever checks DKIM. If a legitimate email gets forwarded (which breaks SPF), a hardfail can cause a legacy receiver to drop it without giving the DKIM signature a chance to save the delivery. Softfail says “flag it, but let DMARC make the final call,” which guarantees both sides of the OR gate get evaluated.
The three flags that matter in the DMARC record: aspf=s and adkim=s for strict alignment, and sp=reject for subdomain policy. Without these, your p=reject policy has gaps that attackers walk through.
Drop third-party senders from SPF entirely
If a third-party email service supports both SPF and DKIM, consider leaving them out of your SPF record altogether. Let DKIM carry the authentication on its own. This removes the SPF path as an attack surface for that sender entirely.
Yes, if something breaks with the DKIM signing configuration, those emails will fail DMARC and won’t be delivered. In my view, that’s a better outcome than keeping an SPF include that lets attackers spoof your domain. A broken DKIM setup is a temporary deliverability problem you’ll notice and fix. An attacker abusing your SPF record is a trust problem you might not notice until the damage is done.
On a related note, some vendors (MailChimp is a well-known example) don’t even support SPF alignment. They send using their own return-path domain, so including them in your SPF record does nothing for DMARC and just burns one of your 10 DNS lookups. If a vendor can’t align SPF, remove them from your record and rely on their DKIM signing instead.
Inbound hygiene matters too
DNS hardening only covers outbound authentication. You also need your email gateway enforcing strict inbound policies on your own domains.
In my experience, services like Mimecast treat your domains as authoritative by default. They won’t accept inbound mail claiming to be from a domain they host unless you create an explicit exception. That’s the right posture. If your gateway accepts anything inbound on your own organisational domains without question, you’re relying entirely on the sender’s authentication to catch spoofed messages, and as we’ve covered, that authentication has gaps.
Configure your email gateway to reject inbound mail from your own domains unless it arrives through a known, legitimate path. This gives you a second layer of protection that doesn’t depend on every third-party sender getting their authentication right.
When Karl asks how attackers bypass DKIM, the answer is straightforward: they find a way to make the server stop asking for it. Tightening your SPF, DMARC, and inbound gateway policies closes that door from both sides.