Skip to content
Back to blog
Network security

How a phishing email gets past SPF, DKIM and DMARC

One fictional phishing email, checked by SPF, DKIM and DMARC: what each verifies, why p=none and look-alike domains let it through, and what to do if you click.

Beginner8 min read

On this page

This is the story of one email. Everything in it is made up: the company, the person, the domains. The domain names end in .example, a top-level domain reserved for documentation by RFC 2606, so none of them can belong to anyone. What is real is the machinery the email passes through on its way to an inbox, and the places where that machinery can be fooled.

Our fictional company is Example Corp, and its mail domain is corp.example. Our fictional reader is Dao, who works there. It is Monday morning, and a new message is waiting.

What Dao sees

Dao's mail app shows three things:

  • From: IT Service Desk
  • Subject: Action needed today
  • Body: "Your password expires in 24 hours. Please confirm here: [Your account]"

That is the whole picture for most of us. A friendly name, a deadline and a button. Notice that the app shows the display name ("IT Service Desk") and hides the actual address unless Dao taps on it. That detail matters later.

What the headers say

Behind the friendly view, every message carries headers. One of them is added by the receiving mail server to record what its checks found. It is called Authentication-Results, and it is defined in RFC 8601. For our fictional email it might look like this:

From: "IT Service Desk" <it-desk@corp.example>
Authentication-Results: mx.corp.example;
  spf=pass smtp.mailfrom=bounce.mailer.example;
  dkim=pass header.d=mailer.example;
  dmarc=fail header.from=corp.example

Two passes and a fail. To understand why, we need to look at each checkpoint in turn.

Checkpoint 1: SPF asks "may this server send for that domain?"

Sender Policy Framework is defined in RFC 7208. A domain owner publishes a DNS TXT record that starts with v=spf1 and lists the servers allowed to send mail for the domain. The receiving server looks at the IP address that connected to it and checks that list.

The key detail is which domain SPF checks. It looks at the HELO name and the envelope sender (MAIL FROM), the address used for bounces, not the From: line Dao sees. The RFC says plainly that these checks do not provide assurance about other identities "such as the From: header field".

So the attacker in our story simply used an envelope address on a domain they control, bounce.mailer.example, and published an SPF record for it. SPF said pass, and it was right: that server is allowed to send for bounce.mailer.example. It just has nothing to do with corp.example.

A few other SPF facts worth knowing:

  • Results include pass, fail, softfail, neutral, none, temperror and permerror (section 2.6).
  • Evaluation may trigger at most 10 DNS-querying terms (include, a, mx and so on); going over gives permerror (section 4.6.4).
  • Forwarding breaks SPF easily, because the forwarder's IP is checked instead of the original sender's (section 10.3).

Checkpoint 2: DKIM asks "did this domain sign this message, unchanged?"

DomainKeys Identified Mail is RFC 6376, an Internet Standard. The sending system adds a DKIM-Signature header containing a hash of the body (bh=), the list of signed headers (h=), the signature itself (b=), the signing domain (d=) and a selector (s=). The receiver fetches the public key from DNS at <selector>._domainkey.<domain> and verifies.

A passing signature proves two things: the domain in d= takes some responsibility for the message, and the signed parts have not changed since signing. It does not say the domain is trustworthy, and the standard does not require the signing identity to match the visible sender.

Our attacker signed the message with their own key for mailer.example. DKIM said pass, and again it was right.

Checkpoint 3: DMARC asks "do those passes belong to the From domain?"

This is the checkpoint that connects the technical checks to what Dao actually sees. DMARC was first published as RFC 7489 in March 2015, an Informational document rather than a standard. In May 2026 it was made obsolete by RFC 9989, a Proposed Standard, which comes with two companion RFCs for reporting, 9990 and 9991. The core idea is the same in both: alignment.

A message passes DMARC only if SPF or DKIM passes and the passing domain aligns with the domain in the From: header. In relaxed mode the two must share an organizational domain; in strict mode they must match exactly (section 3.1).

In our story, From: says corp.example. SPF passed for bounce.mailer.example. DKIM passed for mailer.example. Neither aligns, so DMARC says fail.

Two checkpoints stamp the envelope; DMARC sees that neither passing domain matches the From domain

Why p=none protects nobody

Here is the twist. A failed DMARC check does not stop anything by itself. The receiver looks up the domain owner's policy in a TXT record at _dmarc.corp.example, and the p= tag says what to do:

Policy What the domain owner asks for
p=none No specific action regarding delivery (RFC 7489 §6.3)
p=quarantine Treat failing mail as suspicious, for example the spam folder
p=reject Reject failing mail, ideally during the SMTP conversation

Example Corp, in our story, published p=none and never moved on. The newer RFC 9989 says a discovered p=none policy must not modify existing mail handling processes. So the email that failed DMARC landed in Dao's inbox anyway.

p=none is a useful starting point: the reports (rua=) show who sends mail in your name before you enforce anything. Google, for example, requires bulk senders to Gmail to publish DMARC since 1 February 2024, but accepts a policy of none. Joint guidance from CISA, NSA, FBI and MS-ISAC goes further and recommends that organizations set DMARC to "reject" for the mail they send. Monitoring is a phase, not a destination.

Act two: the attacker changes one letter

Suppose Example Corp fixes its policy and moves to p=reject. Spoofing corp.example directly now fails. So the attacker registers a look-alike domain, c0rp-login.example (a zero instead of an "o"), and sets up SPF, DKIM and DMARC for it properly. Every check now passes, because the mail really does come from c0rp-login.example.

This is not a flaw in DMARC; it is outside its job. RFC 7489 lists "visually similar domain names ("cousin domains")" and abuse of the display name as things it does not address. The display name is free text: anyone can write "IT Service Desk".

That leaves the last layer.

The human layer

CISA's guidance on recognizing phishing lists signs that still hold up well:

  • Urgent or emotional language: especially messages that claim dire consequences if you don't respond immediately. "24 hours" is doing that job in our email.
  • Requests to send personal or financial information.
  • Incorrect email addresses or links: look-alike spellings, such as one wrong letter in a brand name.
  • Untrusted shortened links, which hide the destination.

CISA also notes that poor grammar used to be a common sign, but in the era of AI some phishing emails have perfect grammar and spelling, so the other signs matter more.

The most practical habit is to check where a link really goes. The words on a button and its destination are two separate things. CISA's advice on avoiding phishing suggests hovering over a link before clicking: if the address shown does not match the text, the link may be spoofed.

The link text says one thing; hovering shows the real destination, a look-alike with a zero in place of an &quot;o&quot;

If a message might be genuine, CISA's advice is not to use any link or phone number in it, and to reach the company through contact details from its verified website, or the person through another way you already have, such as their phone number.

The same trick, through many doors

Email is the classic route, but not the only one. MITRE ATT&CK files phishing as technique T1566 under the Initial Access tactic, with four sub-techniques:

ID Name The lure
T1566.001 Spearphishing Attachment A malicious file attached to the message
T1566.002 Spearphishing Link A link to a malicious page or download
T1566.003 Spearphishing via Service Delivered through a third-party service such as social media
T1566.004 Spearphishing Voice A message that gets the victim to call a phone number

ATT&CK notes that phishing can be targeted at a person or company, or sent as a mass campaign. Among its listed mitigations are user training and software configuration, which includes SPF, DKIM and DMARC. In other words, neither layer is expected to work alone.

Dao clicked. Now what?

In our story, Dao clicks, sees a login page that looks right, types a password, and then gets a bad feeling. What happens next matters more than the click.

The UK's National Cyber Security Centre is blunt about this in its phishing guidance for organizations: "Some phishing attacks will always get through", and people should feel able to report "even when they have 'clicked'". It warns against a blame culture, because employees who fear for their jobs will not report mistakes.

What helps, drawing on CISA's advice:

  1. Tell your IT or security team right away. They can be alert for suspicious or unusual activity.
  2. Change the password you typed straight away, and change it on every other account where you used the same one.
  3. Report the message, for example with your mail app's report button. The joint CISA guidance calls reporting one of the most efficient methods for protecting organizations, because it helps email providers spot new or trending phishing attacks.
  4. Watch your accounts for charges or activity you can't explain.

The same joint guidance also recommends FIDO or PKI-based MFA, which it describes as phishing resistant. It warns that weaker forms of MFA can still be defeated through phishing.

The anatomy, in one breath

SPF checks the sending server against the envelope domain. DKIM checks a signature from whichever domain signed. DMARC checks that those results belong to the From: domain, and its policy decides what happens when they don't. None of them checks whether c0rp is corp. That last check belongs to a person, and the kindest thing an organization can do is make it easy and safe for that person to say "I think I clicked something."

Sources

Keep reading