Syntax Recommendations
Common Look Up mechanisms
a:
mx:
include:
ip4:
ip6:
exists:
ptr:
all
Common Modifiers
redirect=
exp=
An A Record must ALWAYS contain IP address (map host to IP)
CNAME (Alias) must contain hostnames. No IPs here
NS an MX records must contain host names. No IPs allowed.
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
View full article