Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1
obsoletes
- rfc-4408 — Sender Policy Framework (SPF) for Authorizing Use of Domains in E-Mail, Version 1
updated by
Extracted elements (40)
SPF uses DNS TXT records rather than a dedicated SPF RR type because the SPFbis working group found that the RFC 4408 dual-RR model was fundamentally flawed: no RR type was simultaneously required to serve and to check, leading to negligible SPF RR adoption and interoperability problems.
The 'include' mechanism name was acknowledged as poorly chosen: it does not literally include mechanisms from another record but only uses the evaluated result. Names such as 'if-match' or 'on-match' would have been more accurate.
The 'redirect' modifier is intended for policy consolidation within a single ADMD (all domain variants share one record), while 'include' is intended for crossing administrative boundaries. Using 'redirect' across administrative domains is unreliable because the referenced record may use local-part-dependent mechanisms that do not apply to the original sender.
When the PTR lookup limit is exceeded, all extra records are ignored rather than producing 'permerror' (unlike the MX limit) because PTR records are controlled by the owner of the connecting IP — potentially an adversary — while MX records are controlled by the publishing ADMD.
Mediators (mailing lists, forwarding services) relay mail from a different IP than the original sender's MAIL FROM domain, causing SPF to fail or be unreliable for forwarded messages. Originating ADMDs should use SRS or similar; receiving ADMDs must account for this limitation when applying local policy.
A compliant SPF implementation MUST produce results semantically equivalent to the check_host() algorithm. Implementations MAY use a different internal algorithm so long as all results are identical.
A domain name MUST NOT have multiple records that would cause an authorization check to select more than one record. If more than one record is found after filtering for 'v=spf1', check_host() produces a 'permerror' result.
A 'neutral' result MUST be treated exactly like the 'none' result. Treating 'neutral' more harshly would discourage ADMDs from testing SPF records, since '?all' is the implicit default for unterminated records.
Any 'redirect' modifier MUST be ignored if an 'all' mechanism appears anywhere in the record. The 'redirect' and 'exp' modifiers MUST NOT each appear more than once; duplicate occurrences cause a 'permerror' result.
Checking identities other than MAIL FROM and HELO against SPF version 1 records is NOT RECOMMENDED because mailing lists rewrite MAIL FROM but may not change other identities, producing incorrect results.
Internationalized domain names in SPF evaluation MUST be encoded as A-labels as described in RFC 5890 Section 2.3. Malformed or non-multi-label domains, and NXDOMAIN responses, cause check_host() to immediately return 'none'.
SPF implementations MUST limit the total number of DNS-querying terms (include, a, mx, ptr, exists, redirect) to 10 during SPF evaluation to avoid unreasonable DNS load. Exceeding this limit MUST return 'permerror'.
SPF implementations SHOULD limit 'void lookups' (DNS queries returning RCODE 0 with zero answers, or RCODE 3) to two per check_host() evaluation. Exceeding this limit produces a 'permerror' result; the default of two is RECOMMENDED when this limit is configurable.
SPF records MUST be published as DNS TXT (type 16) Resource Records only, encoded as US-ASCII. The SPF RR type is no longer supported in SPF version 1 due to its negligible adoption and the fundamental flaw in the dual-RR transition model of RFC 4408.
SPF verifiers MUST check the 'MAIL FROM' identity if a 'HELO' check has not been performed or has not reached a definitive result. When the reverse-path is null, the MAIL FROM identity is defined as 'postmaster@<HELO domain>'.
SPF verifiers SHOULD check the 'HELO' identity in addition to 'MAIL FROM', and checking 'HELO' before 'MAIL FROM' is the RECOMMENDED sequence. Checking 'HELO' can reduce DNS resource usage and is only possible when the HELO string is a valid multi-label domain name.
The 'ptr' mechanism SHOULD NOT be published due to slowness, unreliability under DNS errors, and burden on .arpa servers; however, compliant check_host() implementations MUST support it because it remains part of the protocol.
The Received-SPF header field MUST appear above all other Received-SPF fields in the message. SPF verifiers MUST ensure it contains no invalid characters, is not excessively long, and contains no malicious data provided by the sender.
Unrecognized modifiers MUST be ignored regardless of where or how often they appear in a record, allowing forward compatibility with modifiers defined in future specifications.
When evaluating the 'mx' mechanism, evaluation of each MX record MUST NOT result in querying more than 10 address records (A or AAAA); exceeding this limit MUST produce 'permerror'. For 'ptr', records beyond the first 10 MUST be ignored rather than producing an error.
When rejecting mail due to a 'fail' result during the SMTP transaction, checking software SHOULD use SMTP reply code 550 and enhanced status code 5.7.1. The explanation string returned by check_host() MUST be limited to US-ASCII.
Macros that expand to the sender's local-part (%{l}) or full address (%{s}) cause DNS queries containing private email addresses, exposing them in resolver logs and potentially to third-party DNS operators. The %{p} macro additionally triggers reverse DNS lookups revealing the connecting IP address.
SPF defines seven result values: 'none' (no SPF record or invalid domain), 'neutral' (no assertion made), 'pass' (explicitly authorized), 'fail' (explicitly not authorized), 'softfail' (probably not authorized), 'temperror' (transient DNS error), and 'permerror' (uninterpretable record requiring DNS operator action).
SPF macros allow dynamic expansion in domain specifications using parameters: %{s} (sender), %{l} (local-part), %{o} (sender domain), %{d} (current domain), %{i} (client IP), %{h} (HELO domain), %{v} ('in-addr' or 'ip6'). Uppercase variants produce URL-encoded output. The 'r' transformer reverses dot-split parts; DIGIT transformers select right-hand parts.
SPF mechanisms are: 'all' (always matches), 'include' (recursive check_host() evaluation), 'a' (A/AAAA record match), 'mx' (MX host match), 'ptr' (reverse DNS, deprecated), 'ip4'/'ip6' (explicit IP ranges with optional CIDR), and 'exists' (arbitrary DNS A lookup). Each mechanism has an optional qualifier (+, -, ~, ?) defaulting to +.
The check_host() function takes three arguments — <ip> (SMTP client IP, IPv4 or IPv6), <domain> (the authorizing domain), and <sender> (the MAIL FROM or HELO identity) — and returns one of: none, neutral, pass, fail, softfail, temperror, or permerror.
The 'include' mechanism triggers recursive evaluation of check_host() against a referenced domain; only the result is used (not the referenced record's mechanisms). A recursive 'pass' causes the include to match; 'fail', 'softfail', and 'neutral' cause not-match; 'none' causes 'permerror'.
The 'redirect' modifier replaces the entire SPF record evaluation with that of another domain when no mechanisms match, intended for sharing policy within a single ADMD. The 'exp' modifier provides a human-readable US-ASCII explanation string when a 'fail' result occurs.
This document creates the SPF Modifier Registry, requiring IETF Review for new entries. Initially populated with 'redirect' (Section 6.1) and 'exp' (Section 6.2).
This document notes that the SPF DNS Record Type (type 99) is deprecated and no longer used for SPF version 1. SPF records MUST use TXT (type 16). The historical SPF RR type registration is noted in Section 14.1.
This document registers the Received-SPF mail header field with IANA, as defined in Section 9.1, for recording SPF evaluation results as a trace field in message headers.
An SPF 'pass' for the RFC5321.MailFrom or HELO identity does not authenticate the RFC5322.From header visible to users. Forgers can obtain an SPF pass for their own domain while displaying a different, trusted sender name to the recipient.
On shared hosting where many users send mail through a common MTA, an SPF 'pass' authorizes all users of that MTA, not just the domain owner. SPF cannot distinguish between legitimate and malicious senders sharing an IP address.
Processing limits on DNS lookups (10-term limit, 2-void-lookup limit, 20-second timeout) are essential to prevent denial-of-service attacks via deeply nested 'include' chains or adversarially crafted SPF records that trigger excessive DNS queries.
SPF relies entirely on DNS for authorization data; an attacker who can spoof DNS responses or IP source addresses can defeat SPF checks. Results should be treated as advisory rather than as definitive proof of sender identity.
The 'exp' modifier, external explanation URLs, and macro-expanded data all originate from the publishing domain and are untrusted. Implementations must clearly attribute explanation strings to third parties and must not allow macro-expanded content to inject malicious data into SMTP responses or headers.
An SPF record is a single DNS TXT RR whose RDATA begins with exactly 'v=spf1' (terminated by SP or end of record) followed by space-separated terms. Multiple character-strings within one TXT record are concatenated without spaces. The record grammar is: record = version terms *SP; version = 'v=spf1'.
Directives have the form [qualifier]mechanism, where qualifier is one of '+' (pass), '-' (fail), '~' (softfail), '?' (neutral) and defaults to '+'. Modifiers use 'name=value' syntax. Mechanism and modifier names are case-insensitive.
The ip4 mechanism encodes dotted-quad notation with optional CIDR prefix /0–32 (default /32); the ip6 mechanism encodes RFC 4291 colon notation with optional prefix /0–128 (default /128). Omitting IP address octets instead of using CIDR notation is not permitted.
The Received-SPF header field format is: 'Received-SPF:' [CFWS] result FWS [comment FWS] [key-value-list] CRLF, with defined keys: client-ip, envelope-from, helo, mechanism, problem, receiver, identity. It SHOULD be prepended above the Received: field generated by the SMTP receiver.