Postmortem: Our SPF Record Was Valid, But It Authorized a Phish
A passing DMARC report for a credential harvesting email led us down a rabbit hole of decommissioned vendors and re-registered domains.

The alert came in as a routine user-reported phish. The email was a credential harvesting attempt targeting our finance department, complete with a convincing-looking login portal. But when our junior analyst pulled the headers, triage went off the rails. The `Authentication-Results` header was pristine. `dmarc=pass`. `spf=pass`. Everything screamed legitimate.
This is the kind of alert that causes cognitive dissonance. The email is clearly malicious, yet the authentication protocols designed to stop this exact attack say it's authentic. Your tools tell you one thing, your eyes another. The sender IP had a terrible reputation, belonging to a known abusive network. So how did it get authorized to send on behalf of our domain?
It turns out our SPF record was lying. Not because of a syntax error, but because of a ghost in the machine—a trail of delegated trust we had long forgotten. This is the story of how a syntactically valid record became a semantic backdoor.
Triage Anomaly: A Phish with a Perfect DMARC Pass
In a modern email security stack, DMARC is the final arbiter. When an email arrives, the receiving mail server checks for a DMARC record on the domain found in the `From:` header. Our policy was `p=reject`, the strictest enforcement. For an email to pass DMARC, it must pass either SPF or DKIM, and it must be 'aligned'.
Alignment is the critical piece defined in RFC 7489. For SPF, it means the domain in the SMTP `MAIL FROM` command (the envelope sender) must match the domain in the `From:` header (the message sender). In this case, the phisher was smart. They used our own domain in both places, so alignment was a given. The only question left was authentication. How did they pass the SPF check itself?
Authentication-Results: mta.example.net; dmarc=pass (p=reject sp=reject dis=none) header.from=ourcorp.com; spf=pass (sender IP is 198.51.100.23) smtp.mailfrom=ourcorp.com
That `spf=pass` is the smoking gun. SPF, defined in RFC 7208, is supposed to prevent exactly this. It's a TXT record in our DNS that explicitly lists every server IP address authorized to send mail for `ourcorp.com`. The receiving MTA saw the sender's IP, `198.51.100.23`, fetched our SPF record, and concluded that this IP was on the approved list. But we've never used that IP range. It wasn't in our NetOps inventory, and it wasn't associated with Microsoft 365 or our other primary senders. The authorization had to be coming from somewhere else inside our record.
Root Cause: The Ghost of a Decommissioned Vendor
An SPF record isn't always a simple list of IPs. To manage complexity, it uses `include` statements. An `include` is a directive that tells a receiving mail server to fetch and evaluate another domain's SPF record as part of the check. It's a mechanism for delegation. You're effectively saying, 'Trust whoever `sendgrid.net` trusts'.
Our record looked something like this: `v=spf1 ip4:203.0.113.0/24 include:spf.protection.outlook.com include:sendgrid.net include:old-saas.com -all`. The `ip4` covers our on-premise relay. Outlook is our primary mail system. SendGrid is for marketing newsletters. But then there was `old-saas.com`.
Chasing the DNS Trail
No one on the current team recognized the `old-saas.com` domain. A quick `dig TXT old-saas.com` gave us our answer, and it was a gut punch. Their SPF record was shockingly simple: `v=spf1 ip4:198.51.100.0/24 -all`. It authorized the exact IP subnet our malicious email came from. We were delegating SPF trust to a domain that was, in turn, explicitly authorizing a malicious sender.
Vendor Offboarding Gone Wrong
After a frantic round of queries to procurement and business stakeholders, the story clicked into place. `old-saas.com` was a marketing analytics platform we had trialed and then abandoned nearly two years ago. The contract was terminated, the virtual machine was decommissioned, but a critical offboarding step was missed: no one removed the `include` from our public DNS.
The domain registration for `old-saas.com` had expired six months prior to our incident. An opportunistic threat actor, likely scanning for expired domains present in SPF records, simply re-registered it. For the price of a domain name, they gained the ability to forge email from our domain with perfect SPF and DMARC authentication. We had handed them the keys.
When a 'Valid' SPF Record Is a Loaded Gun
This incident highlights the crucial distinction between a *valid* record and a *correct* one. Online SPF validators would have given our record a clean bill of health. The syntax was correct. It resolved without error. It didn't exceed the 10 DNS lookup limit imposed by RFC 7208. By every technical measure of validity, it was perfect.
But it was not correct. It did not reflect ground truth. The `include` mechanism is fundamentally about transitive trust. You are not just trusting the vendor; you are trusting their security practices, their domain lifecycle management, and the contents of their own DNS records, now and forever. When you stop using a vendor, that trust must be explicitly revoked in your DNS. Otherwise, it becomes a ticking time bomb.
This offboarding failure is a common but dangerous blind spot in supply chain security. We spend immense effort vetting a vendor on the way in, but treat the relationship as terminated once the final invoice is paid. The DNS artifacts, however, can live on indefinitely, creating a persistent and invisible attack surface.
Beyond Syntax: An SPF Record Audit Playbook That Actually Works
If this incident proves anything, it's that a passive approach to SPF management is insufficient. A proper `spf record audit` is an active, investigative process that must go far beyond what an automated linter can do.
Enumerate and Justify Every Mechanism
Your first step is to treat your SPF record like a firewall ruleset. For every single mechanism—`ip4`, `ip6`, `a`, `mx`, and especially `include`—you must be able to answer three questions. What service does this authorize? Who is the internal business owner of that service? When was this entry last reviewed and confirmed necessary? If you can't answer these for an entry, it's a candidate for immediate investigation and potential removal.
Flatten the Chain of Trust
You cannot simply trust your `include`d domains. You must verify what they authorize. This means performing a recursive resolution of your entire SPF chain. Write a script or use a tool to resolve your record, then resolve every `include` within it, and so on, until you have a final, flattened list of every single IP address and range permitted to send mail as you. This is the true representation of your sending authority. If there are IPs in that list you don't recognize and can't justify, your record is incorrect.
Verify Terminating Mechanisms and Lookup Limits
During your flattening process, pay attention to how each included SPF record ends. A strong record will terminate with a `~all` (softfail) or `-all` (fail). If a vendor's record uses a neutral `?all` or simply omits a terminator, it adds a DNS lookup for potentially zero security value. Speaking of lookups, your audit must confirm you remain under the 10-lookup limit. Exceeding this limit causes a `permerror`, leading to legitimate emails failing SPF validation. This forces a degree of hygiene, but it's a limit you must actively manage as you add and remove services.
The takeaway
Your SPF record is not a static piece of IT infrastructure. It is a dynamic policy statement about who you trust to represent your brand in the inbox. It must be managed with the same rigor as user accounts, firewall rules, and privileged access. A stale entry isn't just clutter; it's a latent vulnerability.
The operational cadence for an SPF audit should mirror your other security policy reviews—at least quarterly, and as a mandatory step in any vendor offboarding process. The goal is to ensure the statement in your DNS constantly and accurately reflects the reality of your operations. Automated tools that continuously resolve `include` chains, like the SPF monitoring features in MailSleuth.AI, can turn this periodic chore into a real-time alerting capability, notifying you the moment a delegated domain changes its record or is about to expire. Because the question isn't just whether your SPF record is valid, but whether it's telling the truth.
We dissect phishing campaigns and email infrastructure so you don't have to.


