Sender authentication part 9: SPF Syntax

This is essentially going to be a summary of the information that appears on the OpenSPF documentation web page.  Really, what else can I say that isn't said there?  But, if you're like me and rarely bother clicking on links inside of blog posts and would prefer to read it within the same web page you navigated to, then read on.  For brevity's sake I will split this into a couple of posts.

In my previous posts, I've stated that SPF is a method of authentication where the receiving email host checks to see if the transmitting IP is allowed to send mail for the domain in the envelope sender.  It's actually a bit more complicated than that.  In an SPF record, a domain defines zero or more mechanisms. Mechanisms can be used to describe the set of hosts which are designated outbound mailers for the domain in the envelope sender.

all | ip4 | ip6 | a | mx | ptr | exists | include

Domains may also define modifiers. Each modifier can appear only once.

redirect | exp

Mechanisms

Mechanisms can be prefixed with one of four qualifiers:

"+"  Pass
"-"  Hard Fail
"~"  Soft Fail
"?"  Neutral

If a mechanism results in a hit, its qualifier value is used. The default qualifier is "+", i.e. "Pass". If no mechanism or modifier matches, the default result is "Neutral".

Mechanisms are evaluated in order.

If a domain has no SPF record at all, the result is "None". If a domain has a temporary error during DNS processing, the result is "TempError" (called "Error" in earlier posts and earlier drafts of SPF). If some kind of syntax or evaluation error occurs (eg. the domain specifies an unrecognized mechanism) the result is "PermError" (formerly "Unknown"). 

Sample SPF records

Below are some examples of sample SPF records that a domain could publish using the syntax above.  The first part, v=spf1, means that the version of SPF that the domain has set up for that record is SPF version 1.0.  We will revisit how to interpret the rest of the line in subsequent posts.

"v=spf1 -all"

"v=spf1 a -all"

"v=spf1 a mx -all"

"v=spf1 +a +mx -all"

Evaluation

Evaluation of the SPF record can return any of these results:

Result: Pass
Explanation: The SPF record designates the host to be allowed to send.
Intended action: Accept

This is the type of action I like to see.  This is useful when combined with safe senders as it means that the envelope sender that claims to have sent the mail really did send the mail.

Result: Hard Fail
Explanation: The SPF record has designated the host as NOT being allowed to send.
Intended action: Reject

In Exchange Hosted Services, we have custom delivery options for Hard Fails.  If the customer chooses, we will auto-quarantine SPF Hard Fails.  If they don't opt-in, we add spam points more aggressively but do not automatically send it to spam quarantine.

Result: Soft Fail
Explanation: The SPF record has designated the host as NOT being allowed to send but is in transition.
Intended action: Accept but mark

In Exchange Hosted Services, we add spam points but not as aggressively as a Hard Fail.

Result: None
Explanation: The domain does not have an SPF record or the SPF record does not evaluate to a result.
Intended action: Accept

My own opinion on SPF None differs from OpenSPF.  Rather than having the intended action as accept, I would treat it as "proceed, but we're keeping an eye on you."  I say this because domains without SPF records are more easily exploited (spoofed).

Result: Neutral
Explanation: The SPF record specifies explicitly that nothing can be said about validity.
Intended action: Accept

My opinion on an SPF Neutral is similar to SPF None, but I would not be as suspicious.  At least with SPF Neutral, the owner of the domain took the time to set up SPF records and explicitly stated "We neither confirm nor deny."

Result: PermError (Unknown)
Explanation: A permanent error has occured (eg. badly formatted SPF record).
Intended action: Unspecified

My own opinion on this is the following: Come on, avoid syntax errors in your SPF records.  Sheesh... On the other hand, maybe you're a spammer and you're doing it on purpose.

Result: TempError (Error)
Explanation: A transient error has occured.
Intended action: Accept or Reject

I'm not sure an reject is justified on this action.  Errors can occur because of network hiccups so I wouldn't reject it because of that.  

One thing I'd like to point out is that OpenSPF uses the term "Fail" where I say "Hard Fail."  We call it Hard Fail in our filters and I've gotten used to saying it that way.  However, if I ever say SPF Fail, I mean an SPF Hard Fail and never a Soft Fail.