A Primer on SPF Records
Yesterday there were a million and one web pages on Sender Policy Framework. Today there are a million and two.
When I was researching SPF record syntax for some changes in my email routing, I couldn’t find a single page that gave all the information I needed. I hope this one will save you some time.
Sender Policy Framework is a quick-and-dirty method for using a DNS TXT record for publishing a list of servers that are authorized to send email from a particular domain. Its purpose is to enable recipient MTAs (mail transfer agents, aka email servers) to tell when an email isn’t really coming from who it says it’s coming from.
It is becoming more important all the time for domains to publish SPF records to cut down on spam. If you don’t have an SPF record, anyone in the world can send an email from one of your email addresses, and nobody has any way to tell whether or not it actually came from your email server.
Some people recommend that you publish an SPF record for every domain and host for which you publish an A or MX record. I’m not convinced that it’s necessary to be that thorough.
When the sending server (Let’s call it mx1.domain.com.) initiates a conversation with the recipient server (smtp.anotherdomain.com), it says, “I have an email from joe@domain.com.” The receiving server then checks the SPF record for domain.com. If it says that mx1.domain.com is authorized to send email for the specified domain, then it allows the connection. If mx1.domain.com isn’t on the list, then smtp.anotherdomain.com can reject the email, give it a higher spam score, or take any number of other actions.
SPF records aren’t foolproof. There are a number of circumstances in which they don’t work well.
● SPF records are only a suggestion and not all organizations bother to check for them. An SPF record won’t do any good if a receiving server never looks at it.
● Every organization can process SPF records however they choose. Some will add a tag to the header and increase a message’s spam score. Some will delete the message with no notice. Some will return an NDR (non deliverable report). Some won’t do anything at all.
● There are multiple “from” fields in an email and SPF records only govern one of them, the Return-Path field. Some of the many possible “from” fields are From, Envelope-From, Reply-To, and Resent-From. What makes the “Return-Path” field special is that it contains the email address that was used to make the connection between the sending MTA and the receiving MTA. Most other “from” fields are populated by the email client or some process within the email server, and could still be spoofed.
● Not every anti-spam solution reads SPF records in exactly the same way. Most of the differences are rare and minor, but they still might cause an occassional problem.
● There are limits to the number of mechanisms and servers that can be listed in an SPF record. This becomes a problem when a company uses a number of third party services for services such as marketing, anti-malware, etc. The servers for each of those services needs to be added to the record.
● If it takes too long to run a DNS query on all of the mechanisms in the SPF record, the check will timeout.
● Any syntax errors will cause the entire record to be ignored.
There are 7 possible results of an SPF check:
♦ Pass: The MTA is authorized and the message will be accepted.
♦ Fail: The MTA is not authorized and the message will be rejected.
♦ Neutral: The MTA is neither authorized nor unauthorized and the message should be accepted but subjected to other spam detection methods.
♦ SoftFail: The MTA isn’t authorized, so the message will be tagged but accepted, possibly with a higher spam score.
♦ PermError: The SPF record is malformed or some other error occurred and the message disposition is up to the receiving MTA.
♦ TempError: A transient error has occurred in reading or applying the SPF record and the message disposition is up to the receiving MTA.
♦ None: The MTA has no SPF record or the record doesn’t provide sufficient info. Treated the same as Neutral.
There are 4 main components of an SPF record:
■ Version: This is always the first entry in the record and is always (for now) given as “v=spf1”.
■ Mechanisms: These specify the authorized MTAs using various methods.
■ Qualifiers: Prefixed to a mechanism to give more information on how to treat the listed MTA.
■ Modifiers: Modifiers add special functionality to an SPF record. This is the most easily extensible part of the SPF specification.
There are at least 8 mechanisms that can be used to list a host in an SPF record. There might be others, but I doubt you’ll need to know about them.
► A: Matches the A record of the sending domain. Can include subnet length designators to encompass an ip address range. Example: “a:smtp.domain.com” or simply “a” to specify the host or domain name given in the From address.
► All: Matches all hosts that aren’t explicitly listed elsewhere in the record. Usually listed at the end of the record.
► Exists: Returns successful if any DNS record (e.g. A, CNAME, or MX) exists for the specified host or domain name. Example: “exists:domain.com”.
► Include: Matches the hosts listed in the SPF records of the specified host/domain. Example: “include:spf.protection.outlook.com” for domains hosted by Exchange Online.
► Ip4: Matches an IPv4 network range. If no length is specified, “32” is assumed. Example: “ip4:213.165.64.0/23”.
► Ip6: Matches an IPv6 network range. If no length is specified, “128” is assumed. Example: “ip6:1080::8:800:200C:417A/96”.
► Mx: Matches the MX record of the sending domain or can be used to specify another domain. Example: “mx” or “mx:otherdomain.com”.
► Ptr: Matches the ptr record of the sending domain or can be used to specify another domain. According to openspf.org, this will allow any host whose FQDN ends in the specified domain to send email for the domain. mx1.domain.com, mx2.domain.com, or anyserver.domain.com would all be authorized to send email from any @domain.com address. Example: “ptr” or “ptr:otherdomain.com”.
Each SPF check can only perform 10 DNS lookups. Each A, Exists, Include, MX, and PTR mechanism takes up one of those lookups. Additionally, MX, Include, and Ptr mechanisms can point to multiple additional host names and each of those takes up yet another DNS lookup. Be especially careful with the Include mechanism, because it means you are trusting every server that’s listed in the included SPF record, and you don’t necessarily have any control over that record. If it holds additional Include mechanisms, you could be trusting any number of unknown, foreign MTAs without knowing it.
There are 4 qualifiers that give more detailed instructions concerning each mechanism. You can use a qualifier on any of the 8 mechanisms I listed above. If no qualifier is listed for a mechanism, a + qualifer is assumed.
+ = Pass (Example: “+a:domain.com”)
– = Fail (Example: “-a:otherdomain.com” or “-all”)
? = Neutral (Example: “?include:advertiser.com”)
~ = SoftFail (Example: “~include:questionabledomain.com”)
There are 2 modifiers that you should know about.
○ Exp: Provides a hostname whose TXT record contains an explanation for why a message was rejected. For example, if an SPF record contains “exp=explanation.domain.com” then the receiving MTA that rejects a message can do a DNS query for the TXT record of explanation.domain.com and return the contents of the record with an NDR to the sending MTA. It might say something like “Not authorized to send mail for domain.com.”
○ Redirect: Redirects to the SPF record of another domain or host. In effect, this says “Ignore this record and use this other one instead.” You might use this if your organization uses multiple email domains with identical sending servers and you don’t want to have to update the SPF records for all of them if something changes. Example: “redirect=otherdomain.com”.
You can check an SPF record from a command prompt using nslookup like this:
C:\> nslookup -type=txt domain.com
Here are some sample SPF records from popular email hosts:
☼ hotmail.com
“v=spf1 include:spf-a.outlook.com include:spf-b.outlook.com ip4:157.55.9.128/25
include:spf.protection.outlook.com include:spf-a.hotmail.com include:_spf-ssg-b.microsoft.com include:_spf-ssg-c.microsoft.com ~all”
All these “include” mechanisms can eat up the available 10 DNS lookups very quickly. In fact, I’m not sure how Hotmail’s SPF record even functions.
☼ gmail.com
“v=spf1 redirect=_spf.google.com”
If you lookup _spf.google.com, you’ll get this SPF record:
“v=spf1 include:_netblocks.google.com include:_netblocks2.google.com include:_netblocks3.google.com ~all”
Notice the ~ qualifier on the All mechanism. This means that Google discourages using just any host on the Internet to send mail from a @gmail.com addresses, but they don’t necessarily want to stop you, either.
☼ startmail.com
“v=spf1 include:spf.startmail.com ~all”
Notice that, while gmail.com uses a “redirect” modifier to indicate that their SPF records are stored under a different hostname, startmail.com uses an “include” mechanism. Both methods accomplish the same thing, but there are pros and cons. “Include” uses up one of the ten DNS lookups, while “redirect” doesn’t, even if it actually does require an additional lookup. On the other hand, “include” allows startmail.com to share spf.startmail.com SPF record with one or more other email domains, while adding a few unique hosts to each domain’s own SPF. A “redirect” modifier replaces the original SPF with the new one instead of combining the two. If you lookup spf.startmail.com, you’ll get this SPF record:
“v=spf1 ip4:37.153.204.224/27 ip4:78.108.138.210/32 a:smx-5c0.smtp.startmail.com”
☼ yahoo.com
“v=spf1 redirect=_spf.mail.yahoo.com”
Lookup up _spf.mail.yahoo.com returns:
“v=spf1 ptr:yahoo.com ptr:yahoo.net ?all”
This lets Yahoo be very flexible with naming its email servers, but can result in cascading DNS queries that could quickly exceed the allowed 10 lookups.
☼ my own domain
“v=spf1 include:spf.protection.outlook.com include:_spf.qemailserver.com ?ip4:x.x.x.0/24 ?ip4:x.x.x.x/29 ip4:x.x.x.x/29 ip4:x.x.x.x/26 a:cs25.hostedsecurityprovider.com -all”
I blanked out the ip addresses and replaced one domain name with a made up one for obvious reasons.
“include:spf.protection.outlook.com” allows Microsoft’s Exchange Online servers to send email for my domain.
“include:_spf.qemailserver.com” allows Qualtrics to send surveys to customers and employees using an address on my domain.
“?<ipaddress>/<subnet>” allows a range of servers owned by a partner organization to send email from addresses on our domain. That’s a complicated relationship, and I don’t completely trust every server and user on their domain. I limited these mechanisms as much as I could by specifying segments of their network and by using the “?” qualifier.
“ip4:<ipaddress>/<subnet>” allows a range of servers owned by companies who handle human resources and financial transactions to send email from our domain. I trust their security, so I didn’t prefix these mechanisms with a qualifier. People get very upset if they don’t get these emails.
“a:cs25.hostedsecurityprovider.com” allows a hosted email anti-malware and encryption service to send email for our domain. Since most of our day-to-day messages will be routed through their servers, this mechanism is very important.
“-all” de-authorizes all Internet hosts that are not explicitly included in this SPF record.
Sender Policy Framework is like many other Internet protocols and practices that seem dauntingly opaque until you get beneath the surface. There are complications, to be sure, but overall it’s much simpler than it looks.