Alan Doherty [Rated By ICRA] Level Double-A conformance icon, W3C-WAI Web Content Accessibility Guidelines 1.0
Valid CSS! Valid HTML 4.01 Strict

Alan Doherty's things anyone setting up a sender policy framework record properly should know

or How Not to make the same mistakes everyone else keeps repeating

I'm a Network/IT Consultant based in Dublin and a regular contributer on the SPF help mailinglist.

I have seen a lot of bad SPF records in my time, and spf records seem to consistently be poorly written/understood

So if you are setting up a new SPF or checking up on your existing setup competency here are some tips

all do depend on you actually knowing how you do send ALL legitimate outbound mail

Obviously if unsure please get in touch via the mailinglist or contact me direct rather than guessing, I do this to improve the 'ecosystem' as more (easily recognisable) good senders makes bad ones easier to reject

Do not make others perform unnecessary DNS lookups

Parsers of your spf record have limited resources, do not abuse them, here are some simple and common examples

use of 'ptr'
Using 'ptr' is telling people you have no clue about your mail setup or how to use SPF the very definition is listed as (do not use)
use of 'mx'
Using 'mx' is telling people to lookup your mx records (1 wasted lookup) and inserting a:mx-host-1 a:mx-host-2 etc here, as you are the admin you already know this list of host-names so just put them in yourself and save everyone the hassle, more importantly you may notice that few of these hosts actually can't/shouldn't send outbound email and you may notice many of these listed can be further reduced by the next principle
use of 'a' not followed by :somename and/or use of 'a' followed by :somename-within-your-own-domain
Using 'a' with nothing following basically means where is the sender, thus telling people to lookup's ip(s) and substitute them here (normally the ip of your webserver), as you are you already know these ip(s) (and if they actually can send email) so just put them in yourself and save everyone the hassle, ditto for
there is one exception discussed later when an a:somename-within-your-own-domain can be used to compress an spf record
when to use mx
pretty much never, thus far no sensible use case has been seen
when to use a:somename-outside-your-domain
when the name referanced is a machine outside your control and thus it may be moved at anytime without notice by the 3rd party that operates it, though asking the provider for an appropriate include: SPF record to use instead is worthwhile
when to use a:somename-within-your-domain
when the name used references a large list of ips that signifigantly shortens your SPF txt record, maximum ips seems about 30 per A record (can save 360-630 characters)
multiple hosts vs larger subnet
if you own/operate/trust* the subnet and if your firewalling/setup assures you that you trust all the machines not to send any forgeries potentially originating from this subnet, then allow the whole subnet so later moves/changes of equipment/address' dosn't cause issues, if there are untrusted systems within the subnet that aren't firewalled from connecting to port 25 outbound it is better to list only the trusted ip(s)
count how many DNS lookups are required to parse your whole record (and all subrecords includes/redirects)
this is because once this number reaches 10 most parsers will correctly fail, as thats the normal upper limit
check any SPF include: suggested by a provider/3rd party/ESP
if an esp/3rd party/provider SPF designed to be included by customers has any components that are not ip4:/ip6: or include:further-record then worry about their potential to overload/break your spf record, and try to encourage them to fix
if your spf record is begining to look too long
its hard to define as the max length of a txt record response as it varies with the domain name length,
but if domain-name + spf-txt > 450 characters its gotten too long for a single record
remember you can go to 255 characters in a DNS TXT line, and multiple lines in an spf record some dns editors dont allow multiline entries (use a better provider) but all allow 255
if your provider allows multiline strings then if length-of-domain-name + length-of-string1 + length-of-string2 <450 your SPF record will fit in a 512 byte response and get back to even the oldest most badly setup DNS servers behind the worst broken firewalls
if you have more host address's than can fit in a single record you can chain records via include or redirect, but do it smartly
if your spf record is definitly too long
do you have a lot of ip4:host-ip as opposed to ip4:subnet/mask entries,
would the record be short enough without them?
move them to an a: record as described above up to 30 per A record textually longest first ;)
if you chain, chain smart
Bad Chaining: v=spf1 include:sub-recordA include:sub-recordB -all
Good Chaining: v=spf1 .............................................................................contents-of-subrecord-a..................................................................... include:sub-recordB -all
basically fill the first record to bursting point leaving only enough room for 1 include or redirect to the next, do simmilar with next

spf is not just for envelope sender

setup SPF records for all HELO/EHLOs in use
if you own/operate any of the mailservers in use sending email secure their HELO/EHLO idenity names with SPF records also
setup SPF records for all non email names in your zone
all names not used in helo/ehlo and envelopes of emails should have null spf records, 'v=spf1 -all'

avoid conflicts with poeple who mistakenly still check sender-id (everyone with MS-Exchange/office365)

sender-id (a horrible failed microsoft experiment) abused SPF records as sender-id policies if an explicit sender-id record was not found.
Obviously issues arose because sender-id checks both the envelope-sender AND against the Resent-Sender/Resent-From/Sender/From: header(s) , so an email recieved via a mailinglist/SRS-compliant-forwarder/any indirect method, with a correct envelope of will have the resenders ip checked erroniously against your SPF record if the from: is you@yourdomain.

To avoid having your SPF record mis-used this way you SHOULD define a permissive sender-id policy for PRA , optionally a complete sender-id (but I wouldn't as it further extends the usefull life of the should be dead sender-id protocol and clients)

If you use microsoft exchange disable sender-id 'feature'

if your spf record for states
v=spf1 ip4: -all

this is equivalent to a sender ID mail envelope from policy
spf2.0/mfrom ip4: -all

but to avoid it causing mailing list mail to fail you must define a non failing (ending ?all) pra policy
spf2.0/pra ip4: ?all
in other words positive pass mail from your ips but treat as neutral mail from other ips (don't break mailinglists/forwarders/legit partners/esps)

the best option is just have a open policy for people checking sender-id pra, and ignore other use cases
spf2.0/pra ?all < < < < < < < < This is what I reccomend all domains configure alongside their SPF only

or the tempting option to make all sender-id users filtering fail (but I wouldn't advise it)
spf2.0 +all
aka 'if checking sender-id just accept all forgeries from everyone'

* Trust : can extend beyond machines you own/operate if you trust the owner/operator. example you send via a smarthost(s) ip(s) in providernameX as you already trust the provider and must assume they will assist to stop their other customers from forging email sent as you via this/these same smarthost(s) it is equally safe to trust the providers server netblock (if provider suggests/says no others operate machines within it) as it gives them the ability to move/replace/expand, and gives you no greater attack surface in real terms

Last updated Sept. 2017 Alan Doherty

Please feel free to donate Pizza/Beer/Candy/IoT gadget money to me if any of the free software or advice given was worthwhile to you.

Join the Blue Ribbon Online Free Speech Campaign Stop Spam Harvesters, Join Project Honey Pot block bad bots- [german site]