Email Authentication Explained: SPF, DKIM and DMARC
Email was designed without any way to check who sent a message. The From line is free text, and anything can write anything in it. SPF, DKIM and DMARC are three layers bolted on afterwards to fix that, and understanding what each one actually checks explains why a domain can pass two of them and still be forged.
The hole all three are patching
SMTP, the protocol that moves mail, has two separate notions of who sent a message and neither is verified by default.
There is the envelope sender: the address given during the SMTP conversation, which is where bounces go and which you never see. And there is the From: header: the name and address your mail client displays. They do not have to match. Nothing checks either of them. That single design decision is the reason phishing works at all.
SPF: which servers may send
An SPF record is a TXT record on the domain listing the servers allowed to send mail for it. A receiving server takes the envelope sender's domain, fetches its SPF record, and checks whether the connecting address is on the list.
The record is a sequence of mechanisms ending in a catch-all:
- ip4: and ip6:, specific addresses or ranges.
- a and mx, whatever the domain's own A or MX records point at.
- include:, delegate to another domain's SPF record. This is how a company authorises its mail provider, its marketing platform and its ticketing system, and each include is a supplier named in public.
- -all, everything else fails. ~all, soft fail, treat as suspicious. ?all, neutral, which is the same as having no opinion. +all, authorise the entire internet, which is always a mistake and occasionally a deliberate one.
Two failure modes are worth knowing because they are extremely common. SPF allows a maximum of ten DNS lookups while evaluating a record; exceed it and the result is a permanent error, which most receivers treat as no SPF at all. And a domain may publish only one SPF record, two TXT records both starting v=spf1 invalidate each other, a mistake usually made when a second team adds their own.
SPF checks the envelope, not the From line you can see. An attacker can send from a domain they control, pass SPF for that domain perfectly, and still put your bank's address in the visible From: header. SPF is satisfied and the display is a forgery. This is not an implementation flaw; it is what SPF was scoped to do. Closing that gap is DMARC's job, and it is why SPF alone has never stopped spoofing.
DKIM: a signature that travels with the message
DKIM takes a different approach. The sending server signs the message, a chosen set of headers plus the body, with a private key, and attaches the signature as a header. The matching public key is published in DNS at a name built from a selector: selector._domainkey.example.com. The receiver fetches the key and verifies the signature.
Two fields in the signature matter when you are reading one. d= names the signing domain, which is the identity DKIM actually asserts, and s= names the selector, which lets an organisation run several keys at once, one per provider, rotated independently.
Because the signature is carried inside the message, it survives being forwarded, which SPF does not. It breaks if the content is modified in transit, mailing lists that append footers are the classic case, and the reason ARC exists to let intermediaries vouch for a message they altered.
DMARC: the part that ties the display to the proof
DMARC adds the missing idea: alignment. It requires that the domain in the visible From: header matches the domain that SPF or DKIM actually authenticated. One of the two must pass and be aligned. That is what finally makes the header you read mean something.
The record is a TXT record at _dmarc.example.com, and the fields to read are:
- p=, the policy for failures. none means monitor and deliver anyway. quarantine means treat as spam. reject means refuse outright.
- sp=, a separate policy for subdomains, frequently forgotten and frequently the way in.
- pct=, apply the policy to a percentage of mail, used while rolling out.
- rua= and ruf=, where aggregate and forensic reports are sent. Publishing an rua address is the only way to find out who is sending mail as you.
- aspf= and adkim=, whether alignment is relaxed, allowing subdomains to count, or strict.
p=none is not protection, and most domains never move past it. It is the monitoring stage: failures are reported and delivered anyway. A domain sitting on p=none years after publishing it has the appearance of email authentication and none of the effect, anyone can send mail as that domain and it will land in inboxes. When you are assessing an organisation, the policy value is the finding, not the presence of a record.
How to read a domain's mail posture in thirty seconds
Look up three records and you have a fair picture of how seriously a domain takes being impersonated.
- SPF. Present? Does it end in -all or ~all? How many includes, and who are they, that list is the supplier chain.
- DMARC at _dmarc. The policy value is the answer. Is there an sp=, and does it cover subdomains at the same strength?
- DKIM. Harder to check blind, because you need the selector, which is not discoverable from the zone. Common defaults exist, and a real message from the domain names it in the signature header.
A payment company on p=reject with a tight SPF record has an email security team. A company presenting itself as a bank with no DMARC record at all is telling you something, and it is worth writing down before you go looking for anything subtler.
What none of it stops
This is the part that gets lost, and it is the reason a perfect DMARC record does not end phishing.
- Lookalike domains. DMARC protects your domain. It has nothing to say about a message from a domain that merely resembles yours, which will pass its own authentication perfectly because the attacker owns it and configured it properly.
- Display-name spoofing. The friendly name shown in most clients is free text. A message from an unrelated address with the display name set to your finance director passes every check there is, because nothing about it is forged at the protocol level.
- Compromised real accounts. Mail sent from inside a legitimate, authenticated mailbox is authentic. Thread hijacking, a reply arriving inside a conversation you were already having, is the most convincing phishing there is, and it is fully authenticated mail.
- Everything outside email. Messaging apps, SMS, QR codes and phone calls are unaffected.
Which is why these three records are a floor rather than a defence. They stop your domain being used directly, and they make the remaining attacks take a shape a careful reader can notice. Our guide to spotting phishing covers that shape.
If you are setting it up
The order that avoids blocking your own mail: publish SPF listing every service that sends on your behalf, and end it in ~all to start. Enable DKIM signing at every one of those providers. Publish DMARC at p=none with an rua address, and actually read the reports for a few weeks, they will show senders you had forgotten about, which is the entire point of this stage. Fix what they reveal, then move to quarantine, then to reject. Set sp=reject and publish a null SPF and a reject policy on every domain you own that sends no mail at all, including the ones you bought defensively, unused domains are the ones attackers reach for precisely because nobody protects them.
Check any domain's live SPF, DMARC and other TXT records with the MyRecon domain tool.