Sender authentication part 11: More on SPF Syntax (Continued)

The mx mechanism 

 mx
mx/<prefix-length>
mx:<domain>
mx:<domain>/<prefix-length>

All the A records for all the MX records for domain are tested in order of MX priority. If the client IP is found among them, this mechanism matches.

If domain is not specified, the current-domain is used.

The A records have to match the client IP exactly, unless a prefix-length is provided, in which case each IP address returned by the A lookup will be expanded to its corresponding CIDR prefix, and the client IP will be sought within that subnet.

Example 1

"v=spf1 mx mx:deferrals.domain.com -all"

Check the mx record of the domain in the envelope sender.  Also check the mx-record of deferrals.domain.com, which looks like it is another set of servers whose job is to retry mail for deferring domains.  In any case, if the transmitting IP is found among those A-records, return a Pass.  Otherwise, return a Fail. 

Example 2

"v=spf1 mx/24 mx:offsite.domain.com/24 -all"

Check the mx-record of the current domain and expand it to its /24 CIDR range, and do the same thing with offsite.domain.com (expand it to its /24 CIDR range).  If the transmitting IP is found in the ranges, return a Pass.  Otherwise, return a Fail. 

 
The ptr mechanism 
 ptr
ptr:<domain>

The hostname or hostnames for the client IP are looked up using PTR queries. The hostnames are then validated: at least one of the A records for a PTR hostname must match the original client IP. Invalid hostnames are discarded. If a valid hostname ends in domain, this mechanism matches.

If domain is not specified, the current-domain is used.

If at all possible, this mechanism should be avoided because it will result in a larger number of expensive DNS lookups.

Example 1

"v=spf1 ptr -all"

If this SPF record is specied, we would typically see it in a domain which directly controls all its machines (unlike a dialup or broadband ISP) and allows all its servers to send mail. For example, hotmail.com or paypal.com might do this.

Suppose that the transmitting IP is 4.8.15.16 and the envelope sender is me@lost.com.  The process works as follows:

- The receving mail agent looks up the SPF record for lost.com and sees that it is the above.
- The hostnames for 4.8.15.16 are discovered to be lost.com (acquired by a reverse DNS lookup), island.lost.com and others.lost.com.
- The A-record for lost.com is 4.8.15.13, island.lost.com is 4.8.15.15, and others.lost.com is 4.8.15.16.  The last one matches the original IP.  If none had matched, then the result would have returned a Fail.
- Note that since the envelope domain is lost.com and the reverse DNS all included lost.com, this also constituted a match.

Example 2

"v=spf1 ptr:otherdomain.com -all"

Any server whose hostname ends in otherdomain.com is designated to send mail and returns a pass. Otherwise, return a fail.

 

 The exists mechanism 
 exists:<domain>

Perform an A query on the provided domain. If a result is found, this constitutes a match. It doesn't matter what the lookup result is – it could be 127.0.0.2.

Example 1

In the following example, the client IP is 16.23.42.108 and the current-domain is lost.com.

"v=spf1 exists:example.net -all"

If example.net does not resolve, the result is Fail. If it does resolve, this mechanism results in a match.

 

 The include mechanism 
 include:<domain>

The specified domain is searched for a match. If the lookup does not return a match or an error, processing proceeds to the next directive. Warning: If the domain does not have a valid SPF record, the result is a permanent error. Some mail receivers will reject based on a PermError.

Example 1

In the following example, the transmitting IP is 4.8.15.16 and the current-domain is lost.com.

"v=spf1 include:lost.net -all"

If lost.net has no SPF record, the result is PermError.

Suppose lost.nets SPF record were "v=spf1 a -all". 

Look up the A record for lost.net. If it matches 4.8.15.16, return Pass.  If there is no match, the include as a whole fails to match, but the -all value is not used (from lost.net's SPF record); the eventual result is still Fail from the outer directive set in this example, in this case lost.com.

Note that only the evaluated result of the referenced SPF record is used but its action is not.  The action in the outer directive is what is used.  In the above, if the SPFrecord for lost.com was the following "v=spf1 include:lost.net ~all", then lost.net would still return a Hard Fail, but the overall SPF result would be a Soft Fail.

Furthermore, care is needed to ensure that "include:" mechanisms do not place domains at risk for giving SPF Pass results to messages that result from cross user forgery. Unless technical mechanisms are in place at the specified other domain to prevent cross user forgery, "include:" mechanisms should give a Neutral rather than Pass result. This is done by adding "?" in front of "include:". The example above would be:

"v=spf1 ?include:lost.net -all"

This way, even if the lost.net SPF checks out, we still give it a Neutral because there could be some forging going on.  We neither confirm nor deny the result of the cross-domain checking.