Lock Down the Domains That Never Send Mail: Null SPF + Reject-All DMARC
A domain that sends no email is not secure by inactivity — it is wide open until you say otherwise. Here is the exact four-record DNS lockdown that tells the world your parked domain sends nothing, accepts nothing, and should have every forgery rejected.
EvilMail TeamJuly 19, 20269 min read
A phisher doesn't need to break into anything. They set From: [email protected] — a domain you parked two years ago and forgot about — and hit send. That domain has no MX, no SPF, no DMARC. Gmail and Outlook look it up, find no policy to consult, and drop the message straight into your customer's inbox. The domain "does nothing," and it just phished the people who trust your brand.
The fix costs four DNS records and zero mail infrastructure. The goal is to publish, in machine-readable terms, one unambiguous statement: this domain sends no mail and receives no mail — reject anything that claims otherwise. Absence of records is not neutrality. To a receiver, an empty policy reads as permission.
Why parked and non-sending domains are the soft target
Attackers enumerate spoofable domains at scale. Passive DNS databases and a one-liner against public DMARC lookups let anyone sweep a brand's entire portfolio in minutes, sorting domains into "has enforcement" and "wide open." Your marketing microsite, the typosquat you defensively registered, last year's product-launch domain, the "we only use it for the website" domain with no MX — those are the names with no DMARC record, and that is precisely the list a phishing kit wants.
The mechanics of why it works are simple. When a receiver evaluates an inbound message, it looks up a DMARC policy at _dmarc.<From-domain>
. No record means no policy, and every major receiver treats a missing DMARC record as no enforcement — observe, don't act. There is nothing to fail against, so alignment is moot and the message flows. The domain being "inactive" changes nothing; inactivity is not a signal a receiver can read. Only a published policy is.
SPF's blind spot: envelope vs. header From
The most common mistake is believing v=spf1 -all alone protects your visible address. It does not, and understanding why is the whole game.
SPF authenticates two identities and only two: the MAIL FROM (the RFC 5321 envelope sender, a.k.a. Return-Path) and the HELO/EHLO name the sending server announces. It never inspects the From: header — the RFC 5322 From address your recipient actually sees in their mail client. So a -all SPF record stops an attacker from forging your *envelope* sender while leaving them free to put anything they like in the visible From:. Almost no one ever sees the Return-Path. They see From:, and that is exactly what a spoof needs to control.
DMARC closes this gap. It requires the visible From domain to be aligned with an identity that actually authenticated — either the SPF-validated envelope domain or the DKIM signature's d= domain — and it lets you set the disposition when alignment fails: none, quarantine, or reject. For a parked domain the logic is clean: SPF declares zero authorized senders, DKIM declares no key can ever sign, and DMARC forces the visible-From check and rejects on failure. Add adkim=s and aspf=s so alignment is strict — an exact domain match with no relaxation to the organizational domain, so a subdomain trick can't slip a message through.
The four-record lockdown
A truly silent domain needs four records. Each closes a different door, and each maps to a specific RFC so you're not taking my word for it.
Null MX (RFC 7505) — MX 0 .. The single-dot target (the DNS root) is the standardized way to say "this domain accepts no mail." A conformant sending MTA sees it and returns a 5xx to its own client immediately, instead of queueing and retrying for days. This kills backscatter and stops your parked domain from being used as a bounce-address laundromat.
Hard-fail SPF — v=spf1 -all. Zero mechanisms, hard fail. Use -all, not ~all. Softfail exists to avoid breaking marginal legitimate mail during a migration; a non-sending domain has no legitimate mail to be gentle about, so anything less than hardfail just leaves the door ajar.
Revoked DKIM (RFC 6376) — a wildcard selector *._domainkey publishing v=DKIM1; p=. An empty p= tag means the key is revoked: no selector under this domain can ever produce a validating signature. This slams the DKIM path to alignment shut, so an attacker can't lean on a rogue or replayed signature.
Reject DMARC (RFC 7489) — v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s; rua=.... This is the enforcement layer that ties it together. p=reject sets the disposition, sp=reject extends it to subdomains, strict alignment blocks relaxation tricks, and rua keeps collecting intelligence on who's trying.
Writing the records (the actual zone entries)
In BIND zone format, the entire lockdown for parked.example.com:
dns
; --- parked.example.com: sends nothing, accepts nothing ---
parked.example.com. IN MX 0 .
parked.example.com. IN TXT "v=spf1 -all"
*._domainkey.parked.example.com. IN TXT "v=DKIM1; p="
_dmarc.parked.example.com. IN TXT "v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s; rua=mailto:[email protected]; fo=1"
If your DNS lives at a registrar with a web UI instead of a zone file, enter the same values as flat rows:
text
Name Type Value
@ MX 0 . (priority 0, target ".")
@ TXT v=spf1 -all
*._domainkey TXT v=DKIM1; p=
_dmarc TXT v=DMARC1; p=reject; sp=reject; adkim=s; aspf=s; rua=mailto:[email protected]; fo=1
One rule and one bonus. The rule: exactly one DMARC TXT record is allowed per name — if a receiver finds two records at _dmarc, it discards the policy entirely and you're back to no enforcement. The bonus: the 255-character TXT string-chunking trap that bites long DKIM keys is a non-issue here, because every one of these records is short. Keeping the lockdown minimal is its own reliability feature.
One more record you may need. If your rua= address lives on a different domain than the one being locked down — the common case, where you aggregate all reports to [email protected] — DMARC's external-destination verification (RFC 7489 §7.1) requires the reporting domain to opt in, or Google and Microsoft will silently drop the reports:
dns
parked.example.com._report._dmarc.yourdomain.com. IN TXT "v=DMARC1"
Don't let subdomains leak
p=reject protects the organizational domain. It does not automatically protect mail.parked.example.com or login.parked.example.com — that is what sp=reject is for, and it is not optional on a domain you actually care about.
Two facts drive this. First, SPF is not inherited by subdomains. A subdomain with no SPF record of its own is unprotected on the envelope side, full stop — the parent's -all does nothing for it. Second, DMARC has no wildcard. You cannot publish a _dmarc.* record. The sp= tag on the organizational-domain policy is the only lever you have to set a default disposition for every subdomain that lacks its own explicit _dmarc record. Set it to reject and any name that could appear in a From: header inherits enforcement. Audit with wildcard thinking: if a name resolves and could plausibly show up in a visible From address, it needs coverage, and sp=reject is how a silent domain gets it for free.
Verify with dig (and what "correct" looks like)
Publish, then confirm from a machine that isn't sitting behind your DNS provider's cache. Each command and its expected +short output:
Two things trip people up. Some registrar UIs auto-append the zone name or wrap your value in extra quotes — if dig returns v=spf1 -all.parked.example.com or doubled quotes, that is the UI mangling the record and it won't parse. Second, resolvers cache. Set a 300-second TTL on all four records while you roll out so a mistake corrects in five minutes instead of a day; once dig shows exactly what you expect, raise the TTL back to something sane like 3600 or higher.
Reading the RUA reports — proof it's working
A domain that sends nothing still benefits from aggregate reports, and this is the payoff. Point rua= at a mailbox or a DMARC processor, and within 24 to 48 hours you'll get XML from Google, Microsoft, Yahoo and others listing every source IP that tried to pass mail as your domain — now each one stamped disposition=reject with spf=fail and dkim=fail. That is free, passive threat intelligence on exactly who is targeting your brand and from where, without you running a single mail server.
Keep ruf (per-message forensic reports) optional. Almost no major receiver sends them anymore, and the ones that do include recipient PII, so they carry legal weight you probably don't want. fo=1 asks for a forensic report on any authentication failure, if a receiver supports them — but don't build your monitoring around data that mostly won't arrive. The aggregate rua stream is where the signal is.
Rollout checklist
Inventory every owned domain and subdomain — registrar exports, passive DNS, certificate transparency logs. You can't protect what you forgot you own.
Classify each as sending or non-sending. Be honest; "we might use it someday" is non-sending until the day it isn't.
For each non-sending domain, publish all four records: null MX, v=spf1 -all, empty-p= DKIM wildcard, and p=reject; sp=reject; adkim=s; aspf=s DMARC with a working rua.
Add the _report._dmarc authorization record on the reporting domain if rua points off-domain.
Set TTL 300 on everything during rollout.
Wait one report cycle (24–48h) and confirm the RUA XML shows disposition=reject for spoof attempts.
Raise TTLs once verified.
Re-audit quarterly. Domains expire, new ones get registered, and a defensive registration with no policy is the same open door as the one you just closed.
Put the quarterly audit on a real calendar. The four records don't rot, but your domain portfolio shifts underneath them — and a spoofable domain you registered last month is indistinguishable, to a phisher, from the one you locked down today.