Sender Policy Framework (SPF) is an email validation system designed to prevent email spam by detecting email spoofing, a common vulnerability, by verifying sender IP addresses.

Sender Policy Framework (SPF)

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

RFC 7208 Sender Policy Framework (SPF): Email on the Internet can be forged in a number of ways. In particular, existing protocols place no restriction on what a sending host can use as the "MAIL FROM" of a message or the domain given on the SMTP HELO/EHLO commands. This document describes version 1 of the Sender Policy Framework (SPF) protocol, whereby ADministrative Management Domains (ADMDs) can explicitly authorize the hosts that are allowed to use their domain names, and a receiving host can check such authorization.3



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.

SPF records MUST be published as a DNS TXT (type 16) Resource Record (RR) RFC1035 only. The character content of the record is encoded as US-ASCII. Use of alternative DNS RR types was supported in SPF's experimental phase but has been discontinued.

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

TXT (SPF) Record

Retrieve an existing TXT Record(s)

replace interactpoint.net with any domain you want to lookup

Command-Line / Shell
$ dig interactpoint.net -t txt
interactpoint.net.	300	IN	TXT	"v=spf1 include:spf.protection.outlook.com include:amazonses.com -all"

What does it all mean...

For more details, see SPF Record Syntax and SPF Records

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

Microsoft Office 365 and Amazon SES
include:spf.protection.outlook.com include:amazonses.com

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).

For example
ip4: ip4:

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

Common TXT (SPF) Records

Google G Suite
v=spf1 include:_spf.google.com ~all
Microsoft Office 365
v=spf1 include:spf.protection.outlook.com -all
Amazon SES and Amazon WorkMail
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

Python - pySPF

The pyspf package provides the spf module, a well tested implementation of the of the SPF protocol, which is useful for detecting email forgery.13

This package requires PyDNS (or Py3DNS for running with Python 3) and either the ipaddr or python3.3 and later. PyDNS is available at http://pydns.sourceforge.net. Binary and source RPMs for PyDNS are also available from http://pymilter.sourceforge.net. Py3DNS is available on pypi and at https://launchpad.net/py3dns. The ipaddr module is available from http://code.google.com/p/ipaddr-py or as part of the Python standard library starting with python3.3 (as ipaddress). This package requires authres from either pypi or http://launchpad.net/authentication-results-python to process and generate RFC 5451 Authentication Results headers.14
$ 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[0])

    # status descriptions, https://tools.ietf.org/rfc/rfc4408.txt
    if result[0] == "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[0] == "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[0] == "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[0] == "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[0] == "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 "
    elif result[0] == "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.")
        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()

via the command-line run spf-check.py
(venv)$ python spf-check.py -i -s terry@wayforward.net -c mx1.wayforward.net

$ git add .
$ git commit -m'simple spf checker'

deactivate the virtualenv
(venv)$ deactivate

Last edited by MichaelAlber .
Page last modified on Monday May 20, 2019 16:01:18 UTC.