Syntax Recommendations

Common Look Up mechanisms

a:

mx:

include:

ip4:

ip6:

exists:

ptr:

all

Common Modifiers

redirect=

exp=

 

  1. An A Record must ALWAYS contain IP address (map host to IP)
  2. CNAME (Alias) must contain hostnames. No IPs here
  3. NS an MX records must contain host names. No IPs allowed.
  4. MX records (for mail servers)  should contain hostnames NOT IPs.

Too Many Mechanisms

Section 10.1, "Processing Limits" of the SPF RFC 4408 specifies the following in regards to DNS lookups:

SPF implementations MUST limit the number of mechanisms and modifiers that do DNS lookups to at most 10 per SPF check, including any lookups caused by the use of the "include" mechanism or the "redirect" modifier.  If this number is exceeded during a check, a PermError MUST be returned.  The "include", "a", "mx", "ptr", and "exists" mechanisms as well as the "redirect" modifier do count against this limit.  The "all", "ip4", and "ip6" mechanisms do not require DNS lookups and therefore do not count against this limit. The "exp" modifier does not count against this limit because the DNS lookup to fetch the explanation string occurs after the SPF record has been evaluated.

This limit is in place to prevent SPF lookups from being a useful avenue for Denial of Service attacks.

Using an example SPF record as an example to illustrate, this record was breaking with 12 look-ups:

example.com text = "v=spf1 include:_spf-a.example.com include:_spf-b.example.com include:_spf-c.example.com include:_spf-ssg-a.example.com include:spf-a.anotherexample.com ip4:131.107.115.215 ip4:131.107.115.214 ip4:205.248.106.64 ip4:205.248.106.30 ip4:205.248.106.32 ~all" [ 5 mechanisms]

_spf-a.example.com  text = "v=spf1 ip4:216.99.5.67 ip4:216.99.5.68 ip4:202.177.148.100 ip4:203.122.32.250 ip4:202.177.148.110 ip4:213.199.128.139 ip4:213.199.128.145 ip4:207.46.50.72 ip4:207.46.50.82 a:mh.example.m0.net ~all"  [ +1 = 6 mechanisms]

mh.example.m0.net a = 209.11.164.116

_spf-b.example.com text = "v=spf1 include:spf.messaging.example.com ip4:207.46.22.35 ip4:207.46.22.98 ip4:207.46.22.101 ip4:131.107.1.27 ip4:131.107.1.17 ip4:131.107.65.22 ip4:131.107.65.131 ip4:131.107.1.101 ip4:131.107.1.102 ip4:217.77.141.52 ip4:217.77.141.59 ~all" [+1 = 7 mechanisms]

spf.messaging.example.com text = "v=spf1 include:spfa.anotherexample.com include:spfb.anotherexaple.com include:spfc.anotherexample.com -all"  [+3 = 10 mechanisms]

spfa.anotherexample.com  text = "v=spf1 ip4:157.55.116.128/26 ip4:157.55.133.0/24 ip4:157.55.158.0/23 ip4:157.55.234.0/24 ip4:157.56.112.0/24 ip4:157.56.116.0/25 ip4:157.56.120.0/25 ip4:207.46.100.0/24 ip4:207.46.108.0/25 ip4:207.46.163.0/24 ip4:134.170.140.0/24 ip4:157.56.110.0/23 -all" [+0 = 10 mechanisms]

spfb.anotherexample.com  text = "v=spf1 ip4:207.46.51.64/26 ip4:213.199.154.0/24 ip4:213.199.180.128/26 ip4:216.32.180.0/23 ip4:64.4.22.64/26 ip4:65.55.83.128/27 ip4:65.55.169.0/24 ip4:65.55.88.0/24 ip4:94.245.120.64/26 ip4:131.107.0.0/16 ip4:157.56.73.0/24 ip4:134.170.132.0/24 -all" [+0 = 10 mechanisms]

spfc.anotherexample.com  text = "v=spf1 ip4:207.46.101.128/26 ip6:2a01:111:f400:7c00::/54 ip6:2a01:111:f400:fc00::/54 ip4:157.56.87.192/26 ip4:157.55.40.32/27 ip4:157.56.123.0/27 ip4:157.56.91.0/27 ip4:157.55.206.0/24 ip4:157.55.207.0/24 ip4:157.56.206.0/23 ip4:157.56.208.0/22 -all" [ +0 = 10 mechanisms]

_spf-c.example.com  text = "v=spf1 ip4:203.32.4.25 ip4:213.199.138.181 ip4:213.199.138.191 ip4:207.46.52.71 ip4:207.46.52.79 ip4:131.107.1.18 ip4:131.107.1.19 ip4:131.107.1.20 ip4:131.107.1.48 ip4:131.107.1.56 ip4:86.61.88.25 ip4:131.107.1.44 ip4:131.107.1.37 ~all" [+0 = 10 mechanisms]

_spf-ssg-a.example.com  text = "v=spf1 include:_spf-ssg-b.example.com include:_spf-ssg-c.example.com ~all"  [+2 = 12 mechanisms]

_spf-ssg-b.example.com  text = "v=spf1 ip4:207.68.169.173/30 ip4:207.68.176.1/26 ip4:207.46.132.129/27 ip4:207.68.176.97/27 ip4:65.55.238.129/26 ip4:207.46.222.193/26 ip4:207.46.116.135/29 ip4:65.55.178.129/27 ip4:213.199.161.129/27 ip4:65.55.33.70/28 ~all"  [+0 = 12 mechanisms]

_spf-ssg-c.example.com text = "v=spf1 ip4:65.54.121.123/29 ip4:65.55.81.53/28 ip4:65.55.234.192/26 ip4:207.46.200.0/27 ip4:65.55.52.224/27 ip4:94.245.112.10/31 ip4:94.245.112.0/27 ip4:111.221.26.0/27 ip4:207.46.50.221/26 ip4:207.46.50.224 ~all" [+0 = 12 mechanisms]

spf-a.secondexample.com  text = "v=spf1 ip4:157.55.0.192/26 ip4:157.55.1.128/26 ip4:157.55.2.0/25 ip4:65.54.190.0/24 ip4:65.54.51.64/26 ip4:65.54.61.64/26 ip4:65.55.111.0/24 ip4:65.55.116.0/25 ip4:65.55.34.0/24 ip4:65.55.90.0/24 ip4:65.54.241.0/24 ip4:207.46.117.0/24 ~all" [+0 = 12 mechanisms]

Character String Too Long

255 character limitation in a single string

https://kb.isc.org/article/AA-00356/0/Can-I-have-a-TXT-or-SPF-record-longer-than-255-characters.html

http://www.string-functions.com/length.aspx

You may have more than 255 characters of data in a TXT or SPF record, but not more than 255 characters in a single string.
If you attempt to create an SPF or TXT record with a long string (>255 characters) in it, BIND will give an error 
(e.g. "invalid rdata format: ran out of space".)  Strings in SPF and TXT records should be no longer than 255 characters. 
However to get around this limitation, per RFC 4408 a TXT or SPF record is allowed to contain multiple strings, which
should be concatenated together by the reading application.  In the case of use for SPF (using either TXT or SPF RRs)
the strings are concatenated together without spaces as described below.  Reassembly by other applications of multiple
strings stored in TXT records might work differently.


3.1.3. Multiple Strings in a Single DNS record


As defined in [RFC1035] sections 3.3.14 and 3.3, a single text DNS
record (either TXT or SPF RR types) can be composed of more than one
string. If a published record contains multiple strings, then the
record MUST be treated as if those strings are concatenated together
without adding spaces. For example:

IN TXT "v=spf1 .... first" "second string..."

MUST be treated as equivalent to

IN TXT "v=spf1 .... firstsecond string..."

SPF or TXT records containing multiple strings are useful in
constructing records that would exceed the 255-byte maximum length of
a string within a single TXT or SPF RR record.

EXAMPLE

text = "v=spf1 ip4:199.15.212.0/22 ip4:72.3.185.0/24 ip4:72.32.154.0/24 ip4:72.32.217.0/24 ip4:72.32.243.0/24 ip4:94.236.119.0/26  ip4:37.188.97.188/32 ip4:185.28.196.0/22 ~all“

text = "v=spf1 ip4:199.15.212.0/22 " ip4:72.3.185.0/24 ip4:72.32.154.0/24 ip4:72.32.217.0/24" " ip4:72.32.243.0/24 ip4:94.236.119.0/26" " ip4:37.188.97.188/32 ip4:185.28.196.0/22 ~all"

Null Records in the SPF Record

A record that is NULL or that does not exist will break an SPF record.  Syntax within the record is very important, if there are extra spaces between mechanisms it will count as NULL.

EXAMPLE

text = "v=spf1 ip4:199.15.212.0/22“ <- accurate

text = "v=spf1 ip4: 199.15.212.0/22<- NULL (NOTE the space between IP4: and the IP)

Repetitive Records in the SPF Record - Void Lookups

If there are too many repetitive mechanisms in the SPF record, including records that cascade (for example when using "include:") the record will break.

There is a MAX of 2 void look ups in an SPF record.  More than that and the record will break.  This prevents SPF records from being used in Denial of Service style attacks.

Validation Tools

SPF checker, syntax validator and SPF tester

http://www.kitterman.com/spf/validate.html

SPF checker

http://vamsoft.com/support/tools/spf-policy-tester

SPF validator

http://vamsoft.com/support/tools/spf-syntax-validator

CIDR Calculator

http://www.subnet-calculator.com/cidr.php

Nslookup

http://network-tools.com/nslook/

SPF creation wizard

http://www.microsoft.com/mscorp/safety/content/technologies/senderid/wizard/

Common SPF errors

http://www.openspf.org/FAQ/Common_mistakes

SPF syntax definitions

http://www.openspf.org/SPF_Record_Syntax

Labels (1)