The Sender Policy Framework (SPF) is an open standard specifying a technical method to prevent sender address forgery. More precisely, the current version of SPF — called SPFv1 or SPF Classic — protects the envelope sender address, which is used for the delivery of messages.1
SPF (Sender Policy Framework) is a DNS text entry which shows a list of servers that should be considered allowed to send mail for a specific domain. Incidentally the fact that SPF is a DNS entry can also considered a way to enforce the fact that the list is authoritative for the domain, since the owners/administrators are the only people allowed to add/change that main domain zone.2
Table of contents
- RFC 7208 - Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1
- SPF Record Syntax
- SPF Records
- SPF Tools
- SPF Record Testing Tools
- Best Practices with Sender Policy Framework (SPF)
- Sender Policy Framework Best Practices
The Sender Policy Framework record is added to the Domain Name System (DNS) zone as a text (TXT) record. The Sender Policy Framework record is associated with a domain and specifies which mail server or servers the domain uses to send email.
In 2003, when SPF was first being developed, the requirements for assignment of a new DNS RR type were considerably more stringent than they are now. Additionally, support for easy deployment of new DNS RR types was not widely deployed in DNS servers and provisioning systems. As a result, developers of SPF found it easier and more practical to use the TXT RR type for SPF records.4
replace interactpoint.net with any domain you want to lookup
$ dig interactpoint.net -t txt
;; ANSWER SECTION: interactpoint.net. 300 IN TXT "v=spf1 include:spf.protection.outlook.com include:amazonses.com -all"
The SPF record always starts with the v= element. This indicates the SPF version that is used. Right now the version should always be spf1 as this is the most common version of SPF that is understood by mail exchanges.5
The include mechanism allows you to authorize hosts outside of your administration by specifying their SPF records.6
The ip4 and ip6 mechanism define what IP addresses are allowed to send mail from the domain. This can be a single IP Address or a network range (see Subnet Mask).
The all mechanism matches any address. This is usually used as the last mechanism which defines how to handle any sender IP that did not match the previous mechanisms.7
All mechanisms may specify qualifiers for how to handle a match:8
- + for pass - A "pass" result is an explicit statement that the client is authorized to inject mail with the given identity.9
- - for fail - A "fail" result is an explicit statement that the client is not authorized to use the domain in the given identity.10
- ~ for soft fail - A "softfail" result is a weak statement by the publishing ADMD that the host is probably not authorized. It has not published a stronger, more definitive policy that results in a "fail".11
- ? for neutral - A "neutral" result means the ADMD has explicitly stated that it is not asserting whether the IP address is authorized.12
v=spf1 include:_spf.google.com ~all
v=spf1 include:spf.protection.outlook.com -all
v=spf1 include:amazonses.com ~all
v=spf1 include:sendgrid.net ~all
v=spf1 include:_spf.salesforce.com ~all
v=spf1 include:mailgun.org ~all
v=spf1 include:spf.mailjet.com ~all
v=spf1 include:servers.mcsv.net ~all
- SPF (Sender Policy Framework) implemented in Python
- Python Development Environment
- Python Gitignore
- PyCharm Gitignore
$ mkdir spf-project $ cd spf-project $ git init $ wget -O .gitignore https://raw.githubusercontent.com/github/gitignore/master/Python.gitignore $ which python3.6 $ virtualenv venv -p /usr/bin/python3.6 $ source venv/bin/activate (venv)$ pip install --upgrade pip (venv)$ pip install py3dns (venv)$ pip install authres (venv)$ pip install pyspf (venv)$ pip freeze > requirements.txt (venv)$ git add . (venv)$ git commit -m'Initial pySPF setup'
#!/usr/bin/env python3 """ SPF does email sender validation. For more information about SPF, please see http://www.openspf.net/ """ __author__ = "Michael K. Alber" __version__ = "0.1.0" __license__ = "ALv2" import argparse import spf def main(args): """ Main entry point of the app """ result = spf.check2(i=args.ipaddr, s=args.sender, h=args.client) print("SPF check result: " + result) # status descriptions, https://tools.ietf.org/rfc/rfc4408.txt if result == "pass": print("A 'Pass' result means that the client is authorized to " "inject mail with the given identity. The domain can now, " "in the sense of reputation, be considered responsible for " "sending the message. Further policy checks can now proceed " "with confidence in the legitimate use of the identity.") elif result == "fail": print("A 'Fail' result is an explicit statement that the client is not " "authorized to use the domain in the given identity. The checking " "software can choose to mark the mail based on this or to reject the " "mail outright.") elif result == "neutral": print("The domain owner has explicitly stated that he cannot or does not " "want to assert whether or not the IP address is authorized. A " "'Neutral' result MUST be treated exactly like the 'None' result; the " "distinction exists only for informational purposes.") elif result == "softfail": print("A 'SoftFail' result should be treated as somewhere between a 'Fail' " "and a 'Neutral'. The domain believes the host is not authorized but " "is not willing to make that strong of a statement. Receiving " "software SHOULD NOT reject the message based solely on this result, " "but MAY subject the message to closer scrutiny than normal.") elif result == "temperror": print("A 'TempError' result means that the SPF client encountered a " "transient error while performing the check. Checking software can " "choose to accept or temporarily reject the message. If the message " "is rejected during the SMTP transaction for this reason, the software " "SHOULD use an SMTP reply code of 451 and, if supported, the 4.4.3 DSN " "code.") elif result == "permerror": print("A 'PermError' result means that the domain's published records could " "not be correctly interpreted. This signals an error condition that " "requires manual intervention to be resolved, as opposed to the " "TempError result. Be aware that if the domain owner uses macros " "(Section 8), it is possible that this result is due to the checked " "identities having an unexpected format.") else: print("A result of 'None' means that no records were published by the domain " "or that no checkable sender domain could be determined from the given " "identity. The checking software cannot ascertain whether or not the " "client host is authorized.") if __name__ == "__main__": """ This is executed when run from the command line """ parser = argparse.ArgumentParser() parser.add_argument("-i", "--ipaddr", required=True, help="the ip address of the smtp server") parser.add_argument("-s", "--sender", required=True, help="the email address of the sender") parser.add_argument("-c", "--client", required=True, help="the client sends this command to the SMTP server") args = parser.parse_args() main(args)
(venv)$ python spf-check.py -i 18.104.22.168 -s firstname.lastname@example.org -c mx1.wayforward.net
$ git add . $ git commit -m'simple spf checker'