非官方中文译本声明:本页为 IETF RFC 7208《Sender Policy Framework (SPF) for Authorizing Use of Domains in Email, Version 1》中文译本,由 ztpop.net 整理翻译,仅供学习参考。RFC 文档由 IETF 发布,受 BCP 78 与 IETF 信托法律条款约束;本译本保留原文编号与结构,权威性以英文原文为准。英文原文见 rfc-editor.org/rfc/rfc7208

RFC 7208:电子邮件域名授权使用的发送方策略框架(SPF)第 1 版

摘要

互联网上的电子邮件可以通过多种方式被伪造。特别是,现有协议对发送主机在消息的「MAIL FROM」或 SMTP HELO/EHLO 命令中可以使用什么域名没有任何限制。本文档描述了第 1 版发送方策略框架(Sender Policy Framework,SPF)协议,借助它,管理域(ADMD)可以明确地授权允许使用其域名的那些主机,而接收主机可以检查此类授权。

本文件废除了 RFC 4408。

本备忘录的状态

这是一份互联网标准跟踪(Standards Track)文档。

本文档是互联网工程任务组(IETF)的成果,代表了 IETF 社区的共识,已通过公众审阅并由互联网工程指导组(IESG)批准发布。有关互联网标准的更多信息见 RFC 5741 第 2 节。

有关本文档当前状态、任何勘误以及如何提供反馈的信息,可在 http://www.rfc-editor.org/info/rfc7208 获取。

Copyright (c) 2014 IETF 信托及被列为文档作者的个人。保留所有权利。

本文档受 BCP 78 以及 IETF 信托的《IETF 文档相关法律规定》(http://trustee.ietf.org/license-info)约束,以本文档发布之日生效的版本为准。请仔细审阅这些文档,因为它们描述了您就本文档所享有的权利与限制。从中提取的代码组件必须包含 Simplified BSD License 文本(见信托法律条款第 4.e 节),并按该许可证的约定「不带任何担保」提供。

本文档可能包含 2008 年 11 月 10 日之前已发布或公开可获取的 IETF 文档或 IETF 贡献中的材料。控制其中部分材料版权的个人/实体可能未授予 IETF 信托在该 IETF 标准流程之外修改此类材料的权利。在未取得控制该材料版权的个人/实体充分许可的情况下,本文件不得在 IETF 标准流程之外被修改,也不得在该流程之外创建其衍生作品——除非为将其格式化为 RFC 予以发布,或将其翻译为英语以外的语言。

目录

1. 引言

当前的电子邮件基础设施具有这样一个特性:任何向系统注入邮件的主机都可以在 [RFC5321] 与 [RFC5322] 所指定的各种身份标识中使用它想要的任何 DNS 域名。尽管在某些情形下这一特性是可取的,但它却是减少不请自来的批量邮件(UBE,俗称垃圾邮件)的主要障碍。此外,ADMD(如 [RFC5598] 所述)理所当然地会关注其他实体轻易利用其域名(往往带有恶意意图)的能力。

本文档定义了一种协议,借助它,ADMD 可以授权主机在「MAIL FROM」或「HELO」身份标识中使用其域名。合规的 ADMD 在 DNS 中发布发送方策略框架(SPF)记录,指明哪些主机被允许使用其名称;合规的邮件接收方则利用已发布的 SPF 记录,在邮件事务期间针对使用给定「HELO」或「MAIL FROM」身份标识的发送邮件传输代理(MTA)测试授权。

对邮件接收方而言,还有一个额外的好处:在验证了某个身份标识的使用之后,就可以基于发送方的域名、而非主机的 IP 地址来做出关于该邮件的本地策略决策。这是有利的,因为域名的信誉很可能比主机 IP 地址的信誉更准确——域名在更长的时间内往往更稳定。此外,如果一个声称的身份标识未能通过验证,本地策略就可以对此类邮件采取更强硬的措施,例如拒收。

1.1. 术语

1.1.1. 关键词

本文件中出现的关键词 "MUST"(必须)、"MUST NOT"(不得)、"REQUIRED"(要求)、"SHALL"(应)、"SHALL NOT"(不应)、"SHOULD"(应该)、"SHOULD NOT"(不应该)、"RECOMMENDED"(推荐)、"NOT RECOMMENDED"(不推荐)、"MAY"(可以)和 "OPTIONAL"(可选),均按 [RFC2119] 中的描述进行解释。

1.1.2. 导入定义

ABNF(扩展的巴科斯-诺尔范式)在 [RFC5234] 中定义,其中的记号 "ALPHA"、"DIGIT" 与 "SP"(空格)亦同。

记号 "Local-part"、"Domain" 与 "Mailbox" 在 [RFC5321] 中定义。

"dot-atom"、"quoted-string"、"comment"、"CFWS"(注释折叠空白)、"FWS"(折叠空白)与 "CRLF"(回车/换行)在 [RFC5322] 中定义。

1.1.3. MAIL FROM 定义

本文档关注邮件发送方的身份标识,如 [RFC5321] 所述:

事务以一个给出发送方身份的 MAIL 命令开始。

由于这一身份标识还有许多其他名称,选择一种满足以下条件的名称十分重要:

1. 被广泛使用;

2. 定义良好。

因此,在本文档全文中将使用术语「MAIL FROM」,其定义为 [RFC5598] 中描述的 RFC5321.MailFrom(反向路径)身份标识。

1.1.4. HELO 定义

本文档还使用了 HELO/EHLO 身份标识。「HELO」身份标识源自 SMTP HELO 或 EHLO 命令(见 [RFC5321])。由于 HELO 与 EHLO 在许多情况下可以互换使用,在本文档中它们被统一标识为「HELO」。这意味着 [RFC5598] 中定义的 RFC5321.HELO/.EHLO。这些命令提供了 SMTP 客户端(发送主机)在 SMTP 会话中的身份标识。

1.2. check_host()

第 4 节引入了一种算法,用于针对到达的邮件事务评估 SPF 策略。在早期的实现中,该算法被编码为一个名为 check_host() 的函数。本文档沿用这一名称作为 SPF 评估算法的象征,当然实现者并不要求使用这一名称。

2. 运行概述

2.1. 发布授权

一个符合 SPF 的域会按照第 3 节的说明发布有效的 SPF 记录。这些记录授权相关域名在「HELO」与「MAIL FROM」身份标识中被指定的那些 MTA 使用。

SPF 结果既可用于做出正向(来源已授权)判定,也可用于做出负向(来源未授权)判定。如果 ADMD 选择发布 SPF 记录并希望支持接收方做出负向授权判定,那么它就有必要发布以 "-all" 结尾的记录,或者重定向到同样以 "-all" 结尾的其他记录;否则,就无法做出确定性的授权判定。与负向判定相关的潜在问题及缓解措施在第 10 节讨论。

对于那些希望声明「在 HELO 或 MAIL FROM 命令中没有任何主机被授权使用其 DNS 域名」的 ADMD,可以为那些既不在电子邮件地址的域名部分中使用、也不预期会发起邮件的域名发布相应的 SPF 记录。

在变更 SPF 记录时,必须注意确保存在一个过渡期,使旧策略在几乎所有合法邮件都能被合理地认为已经过检查之前一直保持有效。[RFC5321] 第 4.5.4.1 节讨论了消息在传输中可能停留多长时间。尽管离线检查是可能的,但检查越接近原始传输时间,就越有可能得到与发送方 ADMD 在发送消息时的意图相匹配的 SPF 结果。

2.2. 检查授权

邮件接收方可以对其收到的每封邮件执行一组 SPF 检查。SPF 检查测试的是客户端主机是否经授权以给定身份标识发送邮件。通常,此类检查由接收 MTA 执行,但只要所需信息可用且可靠,也可以在邮件处理链的其他位置执行。「MAIL FROM」与「HELO」身份标识分别按第 2.4 节与第 2.3 节的说明进行检查。

在未获得发布 ADMD 明确批准的情况下,不推荐针对 SPF 第 1 版记录检查其他身份标识,因为已知存在会导致错误结果的情形。例如,几乎所有的邮件列表都会重写「MAIL FROM」身份标识(见第 10.3 节),但其中一些不会更改消息中的任何其他身份标识。定义其他身份标识的文档必须定义取得明确批准的方法。

邮件接收方有可能将 SPF 检查用作对入站邮件进行更大规模测试集合的一部分。其他测试的结果可能会影响是否执行某项特定的 SPF 检查。例如,在本地白名单中发现发送主机的 IP 地址,可能会导致跳过所有其他测试,并接受来自该主机的所有邮件。

当邮件接收方决定执行 SPF 检查时,它必须使用正确实现的 check_host() 函数(第 4 节),并以正确的参数进行评估。尽管作为整体而言该测试是可选的,但一旦决定执行测试,就必须按规范执行,以在发布方与接收方之间保持正确的语义。

为了执行测试,邮件接收方必须按照第 4.1 节描述的参数来评估 check_host() 函数。

尽管无效、格式错误或不存在的域名会因找不到 SPF 记录而导致 SPF 检查返回 "none",但长期以来许多 MTA 的策略都是拒收来自此类域名的电子邮件,尤其是在「MAIL FROM」无效的情况下。拒收电子邮件将阻止规避 SPF 记录的一种方法。

实现必须注意从随 SMTP MAIL FROM 命令提供的数据中正确提取 <domain>,因为许多 MTA 仍然会接受源路由(见 [RFC5321] 附录 C)、%-hack(见 [RFC1123])和 bang path(见 [RFC1983])等古老特性。这些古老特性曾被恶意用于绕过安全系统。

2.3. "HELO" 身份标识

建议 SPF 验证方不仅对「MAIL FROM」身份标识进行检查,还应通过将 check_host() 函数(第 4 节)应用于以「HELO」身份标识作为 <sender> 的方式,单独检查「HELO」身份标识。检查「HELO」有助于提高结果的一致性,并能减少 DNS 资源的使用。如果能够基于「HELO」检查对消息做出结论性判定,那么就可以避免为处理通常更复杂的「MAIL FROM」而消耗 DNS 资源。此外,由于为「HELO」身份标识发布的 SPF 记录指向单一主机,在可用时,它们是主机授权状态非常可靠的来源。如果要同时检查两者,建议先检查「HELO」再检查「MAIL FROM」。

请注意,EHLO 或 HELO 命令中所呈现域名的要求对发送方而言并不总是清晰的,SPF 验证方必须准备好应对该身份标识是 IP 地址字面量(见 [RFC5321] 第 4.1.3 节)或干脆格式错误的情况。只有在「HELO」字符串是一个有效的、多标签域名时,才能执行此 SPF 检查。

2.4. "MAIL FROM" 身份标识

如果「HELO」检查要么尚未执行,要么尚未得出确定性的策略结果,SPF 验证方必须将「MAIL FROM」身份标识作为 <sender> 应用 check_host() 函数来进行检查。

[RFC5321] 允许反向路径为空(见 [RFC5321] 第 4.5.5 节)。在这种情况下,不存在显式的发送方邮箱,此类消息可被假定为来自邮件系统本身的通知消息。当反向路径为空时,本文档将「MAIL FROM」身份标识定义为由本地部分 "postmaster" 与「HELO」身份标识(该身份标识此前可能已被单独检查,也可能没有)组成的邮箱。

2.5. 检查的位置

授权检查应在处理接收邮件的 SMTP 事务期间执行。这降低了确定用作 check_host() 输入的正确的 IP 地址的复杂度,并允许通过 SMTP 应答直接向发送 MTA 返回错误。 [RFC7001] 的附录 D 对该主题进行了更充分的讨论。

授权检查在 SMTP 事务中、MAIL 命令的时刻执行,并使用 MAIL FROM 值与客户端 IP 地址。在其他时间或以其他输入执行检查可能导致如下问题:

o 可能难以从具有潜在欺骗性的信头中准确提取所需信息。

o 由于发送方的策略此后可能已变更,合法邮件可能未通过授权检查。

向未通过授权检查的伪造身份标识生成投递失败通知,往往构成「退信反弹」(backscatter),即无法处理的拒绝通知,会给收件人造成困扰。强烈建议运营方避免此类做法。[RFC3834] 第 2 节描述了退信反弹及其引发的问题。

2.6. 评估结果

第 4 节定义了 check_host(),这是一个模型函数定义,它使用上述输入以及发布在 DNS 中的发送方策略,就客户端授权得出结论。SPF 验证方实现与该处定义的函数在语义上等价的东西。

本节列举并简要定义了该函数的可能输出。但请注意,该协议并未就如何处理任何特定结果建立规范性要求。每种结果的处理选项讨论见第 8 节。

2.6.1. None

"none" 结果意味着:(a) 从未 SMTP 会话中提取到可用于作为被授权对象的语法有效的 DNS 域名;或 (b) 未能从 DNS 中检索到任何 SPF 记录。

2.6.2. Neutral

"neutral" 结果意味着 ADMD 已明确声明:它并未断言该 IP 地址是否已被授权。

2.6.3. Pass

"pass" 结果是一个明确的声明:客户端经授权可以给定身份标识注入邮件。

2.6.4. Fail

"fail" 结果是一个明确的声明:客户端未获授权在给定的身份标识中使用该域名。

2.6.5. Softfail

"softfail" 结果是发布 ADMD 的一个较弱声明:该主机可能未被授权。它尚未发布更强的、更具确定性的、会产生 "fail" 的策略。

2.6.6. Temperror

"temperror" 结果意味着 SPF 验证方在执行检查时遇到了瞬时(通常是 DNS)错误。稍后重试可能无需进一步的 DNS 运营方干预即可成功。

2.6.7. Permerror

"permerror" 结果意味着该域发布的记录无法被正确解释。这发出了一个明确的错误状况信号,确实需要 DNS 运营方干预才能解决。

3. SPF 记录

SPF 记录是一条 DNS 记录,它声明了哪些主机被授权、哪些未被授权在「HELO」与「MAIL FROM」身份标识中使用某个域名。粗略地说,该记录将主机划分为「允许」与「不允许」两个集合(尽管某些主机可能两者都不属于)。

SPF 记录表示为在单条 DNS TXT 资源记录(RR)[RFC1035] 的 RDATA 中找到的单个文本字符串;不允许同一个所有者名称存在多条 SPF 记录。记录格式与记录选择过程在下文第 4 节描述。一个示例记录如下:

v=spf1 +mx a:colo.example.com/28 -all

该记录的版本为 "spf1",并包含三个指令:"+mx"、"a:colo.example.com/28"("+" 为隐含)、以及 "-all"。

每条 SPF 记录都放置在其所属的所有者名称对应的 DNS 树中,而不是放在所有者名称下的子域中。这与 SRV 记录 [RFC2782] 的做法类似。

本节中的示例可能通过域名区域文件中的以下行来发布:

example.com.          TXT "v=spf1 +mx a:colo.example.com/28 -all"

由于 TXT 记录有多种用途,要当心那里为其他目的发布的其他 TXT 记录。它们可能因大小限制(见第 3.4 节)而引发问题,并且必须注意确保只有 SPF 记录被用于 SPF 处理。

发布 SPF 记录的 ADMD 应当尽量将评估一条记录所需的 DNS 信息量保持在最小。第 4.6.4 节与第 10.1.1 节就 "include" 机制与链式 "redirect" 修饰符给出了一些建议。

3.1. DNS 资源记录

SPF 记录必须仅作为 DNS TXT(类型 16)资源记录(RR)[RFC1035] 发布。记录的字符内容以 [US-ASCII] 编码。在 SPF 的实验阶段曾支持替代的 DNS RR 类型,但后来已 discontinue(停用)。

2003 年 SPF 刚开始开发时,分配新 DNS RR 类型的要求比现在要严格得多。此外,对新 DNS RR 类型的轻松部署支持在 DNS 服务器与配置系统中尚未广泛铺开。结果,SPF 的开发者发现使用 TXT RR 类型来承载 SPF 记录更容易、也更切实可行。

在审查 [RFC4408] 时,SPFbis 工作组得出结论:其双 RR 类型过渡模型从根本上就是 flawed(有缺陷的),因为它没有包含一个实现者既被要求提供、又被要求检查的通用 RR 类型。为解决这个问题曾考虑过许多替代方案,但工作组最终认为在可预见的未来向 SPF RR 类型大规模迁移的可能性极低,而解决这一互操作性问题的最佳方案就是从 SPF 第 1 版中放弃对 SPF RR 类型的支持。更多信息见 [RFC6686] 的附录 A。

十年前 SPF 最初部署时的环境是独特的。如果未来开发的 SPF 更新不复用现有的 SPF 记录,它可以使用 SPF RR 类型。SPF 将 TXT RR 类型用于结构化数据,绝不应被未来协议设计者视为先例。关于使用新 DNS RR 类型时的设计考量,进一步讨论见 [RFC5507]。

3.2. 多条 DNS 记录

一个域名不得拥有会导致授权检查选择超过一条记录的多个记录。选择规则见第 4.5 节。

3.3. 单条 DNS 记录中的多个字符串

如 [RFC1035] 第 3.3 节与第 3.3.14 节所定义,单条文本 DNS 记录可以由多个字符串组成。如果一条已发布的记录包含多个字符-字符串,那么该记录必须被视为这些字符串被拼接在一起、且不加空格。例如:

IN TXT "v=spf1 .... first" "second string..."

等价于:

IN TXT "v=spf1 .... firstsecond string..."

包含多个字符串的 TXT 记录,对于构造会超出单条 TXT 记录中字符-字符串 255 字节最大长度的记录非常有用。

3.4. 记录大小

给定域名的已发布 SPF 记录应当保持足够小,使其查询结果能装入 512 字节以内。否则,就有可能超出 DNS 协议限制。这一 UDP 限制在 [RFC1035] 第 2.3.4 节中定义,尽管后来被 [RFC2671] 提高。保持在 512 字节以下应能防止较旧的 DNS 实现回退到 TCP,并在缺少 EDNS0 [RFC6891] 支持的情况下仍能通过 UDP 工作。由于应答大小取决于本文档范围之外的许多因素,只能给出这一指导原则:如果 DNS 消息的大小(即给定类型的所有记录的 DNS 名称与文本的组合长度)低于 450 字节,那么 DNS 应答应当能装入 UDP 数据包。对于过长而无法装入单个 UDP 数据包的记录,可能因防火墙及其他干扰 DNS over TCP 或 ENDS0 运行的问题而被 SPF 验证方静默忽略。

请注意,在计算对 TXT 格式查询的应答大小时,必须考虑在该域名下发布的任何其他 TXT 记录。类似地,与 SPF 相关的所有查询的应答大小都必须评估为能装入单个 512 字节 UDP 数据包(即 DNS 消息大小限制在 450 字节)。

3.5. 通配符记录

不鼓励使用通配符记录进行发布,如果使用则必须小心。如果一个区域包含通配符 MX 记录,它可能希望发布通配符声明,但须满足相同的要求与问题。特别是,该声明必须为任何拥有任何 RR 记录的主机及其子域重复。考虑 [RFC1034] 第 4.3.3 节中的示例。基于此,我们可以这样做:

EXAMPLE.COM.          MX      10      A.EXAMPLE.COM
EXAMPLE.COM.          TXT     "v=spf1 a:A.EXAMPLE.COM -all"

*.EXAMPLE.COM.        MX      10      A.EXAMPLE.COM
*.EXAMPLE.COM.        TXT     "v=spf1 a:A.EXAMPLE.COM -all"

A.EXAMPLE.COM.        A       203.0.113.1
A.EXAMPLE.COM.        MX      10      A.EXAMPLE.COM
A.EXAMPLE.COM.        TXT     "v=spf1 a:A.EXAMPLE.COM -all"

*.A.EXAMPLE.COM.      MX      10      A.EXAMPLE.COM
*.A.EXAMPLE.COM.      TXT     "v=spf1 a:A.EXAMPLE.COM -all"

为了覆盖外发邮件中使用的所有域,SPF 记录必须在区域内的每个名称下列出两次:一次用于该名称本身,一次用通配符覆盖该名称下的树。

4. check_host() 函数

本描述并非应用程序编程接口(API)定义,而是一个用于说明该算法的函数描述。合规的 SPF 实现必须产生与该描述在语义上等价的结果。

check_host() 函数获取 SPF 记录、解析它们并评估它们,以确定某个特定主机是否被允许以给定身份标识发送邮件。执行此检查的接收 ADMD 必须按此处描述正确评估 check_host() 函数。

只要结果在所有情况下都相同,实现可以使用与本处定义的规范算法不同的算法。

4.1. 参数

check_host() 函数接受以下参数:

<ip> —— 发出邮件的 SMTP 客户端的 IP 地址,可以是 IPv4 或 IPv6。

<domain> —— 提供所需授权信息的域;最初为「MAIL FROM」或「HELO」身份标识的域名部分。

<sender> —— 「MAIL FROM」或「HELO」身份标识。

对于递归评估,在最初评估 check_host() 时,<sender> 的域名部分可能与 <domain> 参数不同。在大多数其他情况下它们是相同的(见下文第 5.2 节)。下文第 4.6.4 节描述的 SPF 项的整体 DNS 查找限制,必须作为所有评估的单一全局限制来跟踪,而不仅仅是单次递归评估实例的限制。

请注意,<domain> 参数可能不是格式良好的域名。例如,如果反向路径为空,则使用 EHLO/HELO 域名,并附带其相关问题(见第 2.3 节)。在这些情况下,第 4.3 节定义的 check_host() 会直接返回 "none" 结果。

4.2. 结果

check_host() 函数可以返回第 2.6 节描述的若干结果之一。基于该结果,要采取的行动由接收方的本地策略决定。这将在第 8 节讨论。

4.3. 初始处理

如果 <domain> 格式错误(例如标签长于 63 个字符、非空长度标签不在末尾等),或者不是多标签域名,或者 DNS 查找返回「名称错误」(RCODE 3,也称 "NXDOMAIN" [RFC2308]),那么 check_host() 立即返回 "none" 结果。DNS RCODE 在 [RFC1035] 中定义。格式正确的域名是 [RFC1983] 中定义的完全限定域名。也就是说,在 DNS 中它们相对于根被隐式限定(见 [RFC1034] 第 3.1 节)。国际化域名必须编码为 A-label,如 [RFC5890] 第 2.3 节所述。

如果 <sender> 没有本地部分,则使用字符串 "postmaster" 替代本地部分。

4.4. 记录查找

按照记录的发布方式(见上文第 3 节),需要对 <domain> 名称执行一次 DNS 查询,仅查询 TXT 类型。

如果 DNS 查找返回服务器失败(RCODE 2)或其他错误(RCODE 不是 0 或 3),或者查找超时,那么 check_host() 立即以 "temperror" 结果终止。

4.5. 记录选择

记录以版本节开头:

record           = version terms *SP
version          = "v=spf1"

从查找返回的记录集合开始,丢弃那些不是以恰好 "v=spf1" 开头的版本节的记录。请注意,版本节由 SP 字符或记录末尾终止。例如,版本节为 "v=spf10" 的记录不匹配,将被丢弃。

如果结果记录集合不包含任何记录,check_host() 产生 "none" 结果。如果结果记录集合包含多于一条记录,check_host() 产生 "permerror" 结果。

4.6. 记录评估

check_host() 函数解析并解释 SPF 记录,以针对当前测试找到结果。首先验证记录的语法,如果记录中任何位置存在语法错误,check_host() 会立即以 "permerror" 结果返回,不再进行进一步的解释或评估。

4.6.1. 项评估

共有两类项:机制(第 5 节定义)与修饰符(第 6 节定义)。一条记录按以下扩展的巴科斯-诺尔范式(ABNF)包含这些项的有序列表:

terms            = *( 1*SP ( directive / modifier ) )

directive        = [ qualifier ] mechanism
qualifier        = "+" / "-" / "?" / "~"
mechanism        = ( all / include
                     / a / mx / ptr / ip4 / ip6 / exists )
modifier         = redirect / explanation / unknown-modifier
unknown-modifier = name "=" macro-string
                     ; where name is not any known modifier

name             = ALPHA *( ALPHA / DIGIT / "-" / "_" / "." )

大多数机制允许在名称之后出现 ":" 或 "/" 字符。

修饰符总是在名称之后、任何 ":" 或 "/" 字符(可能是宏字符串的一部分)之前,紧跟着一个等号('=')。

不包含任何 "="、":" 或 "/" 的项是机制,如第 5 节所定义。

按照 [RFC5234] 中 ABNF 记法定义,机制与修饰符名称是大小写不敏感的。

4.6.2. 机制

每个机制从左到右依次考虑。如果没有更多机制,则结果如第 4.7 节所述的默认结果。

当评估一个机制时,会发生三种情况之一:它可以匹配、不匹配,或返回异常。

如果它匹配,处理结束,并将限定符值作为该记录的结果返回。如果它不匹配,处理继续到下一个机制。如果它返回异常,机制处理结束,并返回该异常值。

可能的限定符及其导致 check_host() 返回的结果如下:

"+" pass
"-" fail
"~" softfail
"?" neutral

限定符是可选的,默认为 "+"。

当机制匹配且限定符为 "-" 时,返回 "fail" 结果,并按第 6.2 节所述计算解释字符串。

具体的机制在第 5 节描述。

4.6.3. 修饰符

修饰符不是机制。它们不返回匹配或不匹配。相反,它们提供附加信息。尽管修饰符不直接影响记录的评估,但 "redirect" 修饰符会在所有机制都评估完毕后产生影响。

4.6.4. DNS 查找限制

某些机制与修饰符(统称「项」)在评估时会引发 DNS 查询,有些则不会。会引发 DNS 查询的项有:"include"、"a"、"mx"、"ptr" 与 "exists" 机制,以及 "redirect" 修饰符。SPF 实现必须将这类项的总数在 SPF 评估期间限制为 10,以避免对 DNS 造成不合理的负载。如果超出此限制,实现必须返回 "permerror"。其他项——"all"、"ip4" 与 "ip6" 机制,以及 "exp" 修饰符——在 SPF 评估时不会引发 DNS 查询("exp" 修饰符仅在稍后时间才引发查找),它们的使用不受此限制约束。

在评估 "mx" 机制时,所查询的 "MX" 资源记录数量计入上述 10 个引发 DNS 查找的机制/修饰符的总体限制。除该限制外,每条 "MX" 记录的评估不得导致查询超过 10 个地址记录——"A" 或 "AAAA" 资源记录。如果超出此限制,"mx" 机制必须产生 "permerror" 结果。

在评估 "ptr" 机制或 %{p} 宏时,所查询的 "PTR" 资源记录数量计入上述 10 个引发 DNS 查找的机制/修饰符的总体限制。除该限制外,每条 "PTR" 记录的评估不得导致查询超过 10 个地址记录——"A" 或 "AAAA" 资源记录。如果超出此限制,除前 10 条之外的所有记录都必须被忽略。

两者存在差异的原因在于:MX 记录集合及其内容处于发布 ADMD 的控制之下,而 PTR 记录集合及其内容处于实际建立连接的 IP 地址所有者的控制之下。

这些限制是针对记录中每个机制或宏的,并且附加在上述查找限制之上。

MTA 或其他处理器应当为评估 check_host() 所允许的最大耗时施加一个限制。此类限制应当至少允许 20 秒。如果超出此类限制,授权结果应为 "temperror"。

如第 11.1 节末尾所述,在某些情况下,限制那些 DNS 查询返回「正向应答(RCODE 0)但应答计数为 0」或「名称错误(RCODE 3)应答」的项的数量可能是有用的。这些有时被统称为「无效查找」(void lookups)。SPF 实现应将「无效查找」限制为 2。实现可以选择使此类限制可配置。在这种情况下,推荐默认值为 2。超出限制会产生 "permerror" 结果。

4.7. 默认结果

如果没有任何机制匹配,且不存在 "redirect" 修饰符,那么 check_host() 返回一个 "neutral" 结果,就好像在最后一条指令指定了 "?all" 一样。如果存在 "redirect" 修饰符,check_host() 按第 6.1 节的定义继续。

最好使用 "redirect" 修饰符或 "all" 机制来显式地终止处理。尽管每条未被显式终止的记录末尾都隐含着一个 "?all",但当它被显式提供时,有助于调试工作。例如:

v=spf1 +mx -all

v=spf1 +mx redirect=_spf.example.com

4.8. 域规范

这些机制与修饰符中有几个带有 <domain-spec> 节。<domain-spec> 字符串要经过宏展开(见第 7 节)。结果字符串是完全限定 DNS 名称的通用表示形式:一系列以句点分隔的标签。本文档其余部分将此域称为 <target-name>(目标名称)。

注意:宏展开的结果不再进行任何转义。因此,这一机制无法产生 DNS 标签中合法的所有字符(例如控制字符)。不过,这一机制强大到足以表达合法的主机名以及 DNS 中使用的常见实用标签(如 "_spf")。

对于若干机制而言,<domain-spec> 是可选的。如果未提供,则 check_host() 参数中的 <domain>(见第 4.1 节)被用作 <target-name>。"domain" 与 <domain-spec> 在宏展开后语法相同。"domain" 是 check_host() 的输入值,而 <domain-spec> 由 check_host() 计算。

对带有语法无效域的 check_host() 评估结果未定义。

注意:本文档及其前身没有为正确处理语法无效的 <domain-spec>(可能是宏展开的结果)做出规定,见 [RFC1035]。示例包括带有空标签的名称(如 "foo..example.com")以及长于 63 个字符的标签。一些实现选择将此类错误视为不匹配,从而忽略这些名称;而另一些则返回 "permerror" 异常。

5. 机制定义

本节定义了两类机制:基础语言框架机制与指定发送方机制。

基础机制为语言框架做出贡献。它们不指定特定类型的授权方案。基础机制如下:

all
include

指定发送方机制用于标识一组 <ip> 地址,将其标记为被允许或不被允许使用 <domain> 发送邮件。指定发送方机制如下:

a
mx
ptr (do not use)
ip4
ip6
exists

以下约定适用于所有在任何时刻对 <ip> 与某个 IP 地址进行比较的机制:

如果指令中未给出 CIDR 前缀长度,则 <ip> 与 IP 地址按相等进行比较。(此处 CIDR 是无类别域间路由,见 [RFC4632]。)

如果指定了 CIDR 前缀长度,则仅比较 <ip> 与 IP 地址的指定数量的高位比特是否相等。

当任何机制获取主机地址以与 <ip> 比较时,若 <ip> 为 IPv4,则获取 "A" 记录;若 <ip> 为 IPv6 地址,则获取 "AAAA" 记录。IPv6 服务器上的 SPF 实现需要同时处理 "AAAA" 与 "A" 记录,以应对位于 IPv4 映射的 IPv6 地址 [RFC4291] 上的客户端。IPv4 的 <ip> 地址只能使用 "ip4" 机制列在 SPF 记录中。

若干机制依赖于从 DNS 获取的信息。对于这些 DNS 查询,除非另有说明,如果 DNS 服务器返回错误(RCODE 不是 0 或 3)或查询超时,该机制停止,最外层的 check_host() 返回 "temperror"。如果服务器返回「名称错误」(RCODE 3),则该机制的评估继续,就好像服务器返回了无错误(RCODE 0)且零应答记录一样。

5.1. "all"

all              = "all"

"all" 机制是一个永远匹配的检测。它被用作记录中最右侧的机制,以提供显式的默认。

例如:

v=spf1 a mx -all

"all" 之后的机制将永远不会被测试。"all" 之后列出的机制必须被忽略。当记录中存在 "all" 机制时,必须忽略任何 "redirect" 修饰符(第 6.1 节),无论各项的相对排序如何。

5.2. "include"

include          = "include"  ":" domain-spec

"include" 机制触发对 check_host() 的递归评估。

1. <domain-spec> 按第 7 节展开。

2. check_host() 以结果字符串作为 <domain> 进行评估。<ip> 与 <sender> 参数与当前 check_host() 评估中相同。

3. 递归评估返回匹配、不匹配或错误。

4. 如果它返回匹配,则使用 "include" 机制的相应结果(例如,include 或 +include 产生 "pass" 结果,-include 产生 "fail")。

5. 如果它返回不匹配或错误,父 check_host() 按下表恢复处理,并恢复之前的 <domain> 值。

事后看来,"include" 这个名字起得很糟。只有被引用的 SPF 记录的评估结果被使用,而非字面上将被引用记录的机制包含到第一条记录中。例如,在被引用记录中评估 "-all" 指令并不会终止整体处理,也不必然导致整体 "fail"。(这个机制本可以叫 "if-match"、"on-match" 等更好的名字。)

"include" 机制使得一个域能够指定多个管理上相互独立的域。例如,虚荣域 "example.net" 可能使用管理上独立的域 example.com 与 example.org 的服务器发送邮件。

Example.net 可以说:

IN TXT "v=spf1 include:example.com include:example.org -all"

这将指示 check_host() 实际上去检查 example.com 与 example.org 的记录,以取得 "pass" 结果。只有当该主机不被这两个域中的任何一个允许时,结果才会是 "fail"。

该机制是匹配、不匹配还是返回异常,取决于 check_host() 递归评估的结果:

递归 check_host() 结果导致 "include" 机制
pass匹配
fail不匹配
softfail不匹配
neutral不匹配
temperror返回 temperror
permerror返回 permerror
none返回 permerror

"include" 机制旨在跨越管理边界。当仍处于同一管理权限内时,"include" 通常并非最佳选择。例如,如果 example.com 与 example.org 由同一实体管理,并且两个域的允许主机集合都是 "mx:example.com",那么 example.org 可以指定 "include:example.com",但更可取的是指定 "redirect=example.com" 甚至 "mx:example.com"。

借助 "include" 机制,可以授权一组管理上外部的的主机,但发送方策略的确定仍然是原始域的 SPF 记录(由该记录中的 "all" 机制决定)的功能。"redirect" 修饰符更适合将授权与策略整合到一个要在 ADMD 内部共享的通用集合中。Redirect 更像是一个要在单个 ADMD 的记录之间共享的通用代码元素。可以从单条记录出发,控制任意数量域的授权主机与策略。

5.3. "a"

当 <ip> 是 <target-name> 的 IP 地址之一时,该机制匹配。为清晰起见,这意味着 "a" 机制也匹配 AAAA 记录。

a                = "a"      [ ":" domain-spec ] [ dual-cidr-length ]

使用适合连接类型(IPv4 或 IPv6)的查找类型(A 或 AAAA),对 <target-name> 执行地址查找。将 <ip> 与返回的地址进行比较。如果任何地址匹配,则该机制匹配。

5.4. "mx"

当 <ip> 是某个域名的 MX 主机之一时,该机制匹配。

mx               = "mx"     [ ":" domain-spec ] [ dual-cidr-length ]

check_host() 首先对 <target-name> 执行 MX 查找。然后它对返回的每个 MX 名称执行地址查找。将 <ip> 与每个返回的 IP 地址进行比较。为防止拒绝服务(DoS)攻击,必须遵循第 4.6.4 节定义的处理限制。如果超出 MX 查找限制,则返回 "permerror" 并终止评估。如果任何地址匹配,则该机制匹配。

关于隐式 MX 的注意:如果 <target-name> 没有 MX 记录,check_host() 不得通过查询同一名称的 A 或 AAAA 记录来应用 [RFC5321] 的隐式 MX 规则。

5.5. "ptr"(请勿使用)

该机制测试 <ip> 的 DNS 反向映射是否存在,并正确地指向某个特定域内的域名。不应发布此机制。更多信息见本节末尾的注释。

ptr              = "ptr"    [ ":" domain-spec ]

<ip> 的名称使用以下过程查找:

o 对 <ip> 执行 DNS 反向映射:如果地址是 IPv4 地址,则在 "in-addr.arpa." 中查找相应的 PTR 记录;如果是 IPv6 地址,则在 "ip6.arpa." 中查找。

o 对于返回的每个记录,通过查找其 IP 地址来验证域名。为防止 DoS 攻击,必须应用第 4.6.4 节定义的 PTR 处理限制。如果超出限制,则处理终止,该机制不匹配。

o 如果 <ip> 位于返回的 IP 地址之中,则该域名通过验证。

检查所有通过验证的域名,看它们是否与 <target-name> 域匹配或是 <target-name> 域的子域。如果任一匹配,该机制匹配。如果找不到任何通过验证的域名,或者没有任何通过验证的域名匹配或是 <target-name> 的子域,该机制匹配失败。如果在执行 PTR RR 查找时发生 DNS 错误,该机制匹配失败。如果在执行 A RR 查找时发生 DNS 错误,则跳过该域名并继续搜索。

当满足以下条件时,该机制匹配:

o <target-name> 是通过验证的域名的子域;或

o <target-name> 与某个通过验证的域名相同。

例如,"mail.example.com" 位于域 "example.com" 之内,但 "mail.bad-example.com" 不在。

注意:该机制速度慢,在 DNS 错误情形下不如其他机制可靠,并且对 .arpa 名称服务器造成了巨大负担。如果使用,必须为该域的主机配置正确的 PTR 记录,并且 "ptr" 机制应当是最后被检查的机制之一。经过多年 SPF 部署经验,结论是它已无必要,应当使用更可靠的替代方案。不过,它仍作为 SPF 协议的一部分在使用,因此合规的 check_host() 实现必须支持它。

5.6. "ip4" 与 "ip6"

这些机制测试 <ip> 是否包含在给定的 IP 网络中。

ip4              = "ip4"      ":" ip4-network   [ ip4-cidr-length ]
ip6              = "ip6"      ":" ip6-network   [ ip6-cidr-length ]

ip4-cidr-length  = "/" ("0" / %x31-39 0*1DIGIT) ; value range 0-32
ip6-cidr-length  = "/" ("0" / %x31-39 0*2DIGIT) ; value range 0-128
dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ]

ip4-network      = qnum "." qnum "." qnum "." qnum
qnum             = DIGIT                 ; 0-9
                     / %x31-39 DIGIT       ; 10-99
                     / "1" 2DIGIT          ; 100-199
                     / "2" %x30-34 DIGIT   ; 200-249
                     / "25" %x30-35        ; 250-255
           ; as per conventional dotted-quad notation, e.g., 192.0.2.0

ip6-network      = <as per Section 2.2 of [RFC4291]>
           ; e.g., 2001:db8::cd30

将 <ip> 与给定网络进行比较。如果 CIDR 前缀长度的高位比特匹配,则该机制匹配。

如果省略 ip4-cidr-length,则取为 "/32"。如果省略 ip6-cidr-length,则取为 "/128"。不允许通过省略 IP 地址的部分来替代使用 CIDR 记法。也就是说,使用 192.0.2.0/24 而非 192.0.2。

5.7. "exists"

该机制用于构造一个任意域名,用于 DNS A 记录查询。它允许涉及邮件信封任意部分的复杂方案来确定允许什么。

exists           = "exists"   ":" domain-spec

<domain-spec> 按第 7 节展开。结果域名用于 DNS A RR 查找(即使连接类型是 IPv6)。如果返回任何 A 记录,该机制匹配。

域可以使用该机制指定任意复杂的查询。例如,假设 example.com 发布以下记录:

v=spf1 exists:%{ir}.%{l1r+-}._spf.%{d} -all

<target-name> 可能展开为 "1.2.0.192.someuser._spf.example.com"。这使得可以在用户级别和客户端 IP 地址级别做出细粒度决策。

6. 修饰符定义

修饰符是提供附加信息的名称/值对。修饰符总是有一个 "=" 来分隔名称与值。

本文档定义的修饰符("redirect" 与 "exp")应当出现在记录的末尾、所有机制之后,尽管在语法上它们可以出现在记录中的任何位置。这两个修饰符的排序无关紧要。这两个修饰符各自在记录中不得出现超过一次。如果它们出现超过一次,则 check_host() 以 "permerror" 结果退出。

无法识别的修饰符无论出现在记录中的何处、出现多少次,都必须被忽略。这使得符合本文档的实现能够优雅地处理带有其他规范中定义的修饰符的记录。

6.1. redirect:重定向查询

"redirect" 修饰符旨在将授权与策略整合到一个要在单个 ADMD 内共享的通用集合中。可以从单条记录出发,控制任意数量域的授权主机与策略。

redirect         = "redirect" "=" domain-spec

如果所有机制都匹配失败,并且存在 "redirect" 修饰符,则处理按如下方式进行:

redirect 节的 <domain-spec> 部分按第 7 节的宏规则展开。然后以结果字符串作为 <domain> 评估 check_host()。<ip> 与 <sender> 参数与当前 check_host() 评估中相同。

这一新的 check_host() 评估的结果随后被视为当前评估的结果,例外情况是:如果没有找到 SPF 记录,或者 <target-name> 格式错误,则结果为 "permerror" 而非 "none"。

注意,新查询的域本身可以指定重定向处理。

这一设施适用于希望将同一条记录应用于多个域的组织。例如:

la.example.com. TXT "v=spf1 redirect=_spf.example.com"
ny.example.com. TXT "v=spf1 redirect=_spf.example.com"
sf.example.com. TXT "v=spf1 redirect=_spf.example.com"
_spf.example.com. TXT "v=spf1 mx:example.com -all"

在此示例中,来自三个域中任何一个的邮件都由同一条记录描述。这可以成为一个管理上的优势。

注意:一般而言,域 "A" 不能可靠地使用到另一个不在同一管理控制之下的域 "B" 的重定向。由于 <sender> 保持不变,无法保证域 "B" 处的记录能正确适用于域 "A" 中的邮箱,尤其是当域 "B" 使用涉及本地部分的机制时。"include" 指令通常更为合适。

为清晰起见,任何 "redirect" 修饰符都应当作为记录中的最后一项出现。如果记录中任何位置存在 "all" 机制,则必须忽略任何 "redirect" 修饰符。

6.2. exp:解释

explanation      = "exp" "=" domain-spec

如果 check_host() 由于机制匹配(例如 "-all")而得到 "fail",且存在 "exp" 修饰符,则如下所述计算返回的解释字符串。如果不存在 "exp" 修饰符,则必须向调用应用程序返回默认解释字符串或空解释字符串。

<domain-spec> 经过宏展开(见第 7 节)后成为 <target-name>。获取 <target-name> 的 DNS TXT RRset。

如果存在任何 DNS 处理错误(任何非 0 的 RCODE),或者没有返回记录,或者返回了多于一条记录,或者解释字符串中存在语法错误,则按未给出 "exp" 修饰符的方式处理。

获取的 TXT 记录的字符串不加空格地拼接,然后作为 explain-string 处理,并再次进行宏展开。这一最终结果就是解释字符串。实现可以将最终解释字符串的长度限制在一定范围内,以适应其他协议约束和/或合理的处理限制。由于解释字符串旨在用于 SMTP 应答,且 [RFC5321] 第 2.4 节规定应答采用 [US-ASCII],因此解释字符串必须限制为 [US-ASCII]。

评估 check_host() 的软件可以使用此字符串,以简短消息或 URL 的形式从发布域传递信息。软件应当清楚地表明解释字符串来自第三方。例如,它可以在解释前加上宏字符串 "%{o} explains: ",如第 8.4 节示例所示。

假设 example.com 有以下记录:

v=spf1 mx -all exp=explain._spf.%{d}

以下是 explain._spf.example.com 处一些可能的解释 TXT 记录:

"Mail from example.com should only be sent by its own servers."

   -- a simple, constant message

"%{i} is not one of %{d}'s designated mail servers."

   -- a message with a little more information, including the
      IP address that failed the check

"See http://%{d}/why.html?s=%{S}&i=%{I}"

   -- a complicated example that constructs a URL with the
      arguments to check_host() so that a web page can be
      generated with detailed, custom instructions

注意:在递归进入 "include" 机制期间,不得使用来自 <target-name> 的 "exp" 修饰符。相反,在执行 "redirect" 修饰符时,不得使用来自原始域的 "exp" 修饰符。这是因为 "include" 旨在跨越管理边界,所提供的解释应当是接收 ADMD 给出的那个;而 "redirect" 旨在作为在 ADMD 内整合策略记录的工具,因此重定向的解释应当具有优先地位。

7. 宏

在评估 SPF 策略记录时,某些字符序列旨在被替换为消息或连接的参数。这些字符序列被称为「宏」。

7.1. 形式化规范

宏的 ABNF 描述如下:

domain-spec      = macro-string domain-end
domain-end       = ( "." toplabel [ "." ] ) / macro-expand

toplabel         = ( *alphanum ALPHA *alphanum ) /
                   ( 1*alphanum "-" *( alphanum / "-" ) alphanum )
alphanum         = ALPHA / DIGIT

explain-string   = *( macro-string / SP )

macro-string     = *( macro-expand / macro-literal )
macro-expand     = ( "%{" macro-letter transformers *delimiter "}" )
                     / "%%" / "%_" / "%-"
macro-literal    = %x21-24 / %x26-7E
                     ; visible characters except "%"
macro-letter     = "s" / "l" / "o" / "d" / "i" / "p" / "h" /
                     "c" / "r" / "t" / "v"
transformers     = *DIGIT [ "r" ]
delimiter        = "." / "-" / "+" / "," / "/" / "_" / "="

"toplabel" 构造受字母-数字-连字符(LDH)规则以及额外的顶级域(TLD)限制约束。背景见 [RFC3696] 第 2 节。

一些特殊情况:

o 字面量 "%" 由 "%%" 表示。

o "%_" 展开为单个 " "(空格)。

o "%-" 展开为 URL 编码的空格,即 "%20"。

7.2. 宏定义

在项参数中会展开以下宏字母:

s = <sender>
l = <sender> 的 local-part
o = <sender> 的 domain
d = <domain>
i = <ip>
p = <ip> 的已验证域名(请勿使用)
v = 如果 <ip> 为 ipv4 则为字符串 "in-addr",如果为 ipv6 则为 "ip6"
h = HELO/EHLO 域名

<domain>、<sender> 与 <ip> 在第 4.1 节定义。

以下宏字母仅允许出现在 "exp" 文本中:

c = SMTP 客户端 IP(易读格式)
r = 执行检查的宿主机的域名
t = 当前时间戳

7.3. 宏处理细节

后面不跟随 '{'、'%'、'-' 或 '_' 字符的 '%' 字符是语法错误。因此:

-exists:%(ir).sbl.example.org

是不正确的,将导致 check_host() 产生 "permerror"。相反,以下是合法的:

-exists:%{ir}.sbl.example.org

可选的转换器如下:

*DIGIT = 零个或多个数字

'r'    = 反转值,默认按点拆分

如果提供了转换器或分隔符,则宏字母的替换值会被拆分为由指定的一个或多个分隔符字符分隔的部分。在执行任何反转操作和/或移除左侧部分之后,各部分使用 "." 重新连接,而非原始的分隔字符。

默认情况下,字符串按 "."(点)拆分。注意,对输入字符串中的前导、尾随或连续分隔符不作特殊处理,因此部分列表可能包含空字符串。一些较旧的 SPF 实现禁止域名中的尾随点,因此不应发布尾随点,尽管符合本文档的实现必须接受它们。宏可以指定用于替代 "." 的分隔符字符。

"r" 转换器表示反转操作:如果客户端 IP 地址为 192.0.2.1,则宏 %{i} 展开为 "192.0.2.1",而宏 %{ir} 展开为 "1.2.0.192"。

DIGIT 转换器表示在可选反转之后要使用的最右侧部分的数量。如果指定了 DIGIT,该值必须非零。如果未指定任何 DIGIT,或者该值指定的部分多于可用的部分,则使用所有可用部分。如果 DIGIT 为 5,但只有 3 个部分可用,则宏解释器会假设 DIGIT 为 3。实现必须至少支持值 127,因为那是域名中标签的最大数量(减去末尾的零长度标签)。

"s" 宏展开为 <sender> 参数。它是一个带有 local-part、"@" 字符和域的电子邮件地址。"l" 宏仅展开为 local-part。"o" 宏仅展开为域部分。注意,由于 "include" 和/或 "redirect" 的递归与链式评估,这些值在递归与链式评估期间保持不变。还要注意,如果原始 <sender> 没有 local-part,则在初始处理中将 local-part 设为 "postmaster"(见第 4.3 节)。

对于 IPv4 地址,"i" 与 "c" 宏都展开为标准的点分四组格式。

对于 IPv6 地址,"i" 宏展开为点格式地址;它旨在用于 %{ir}。"c" 宏可以展开为 [RFC4291] 第 2.2 节中指定的任何十六进制冒号格式地址。它旨在供人类阅读。

"p" 宏展开为 <ip> 的已验证域名。查找已验证域名的过程在第 5.5 节定义。如果 <domain> 出现在已验证域名列表中,则应使用该名称。否则,如果存在 <domain> 的子域,则应使用它。否则,可以使用列表中的任何名称。如果没有已验证域名或发生 DNS 错误,则使用字符串 "unknown"。

不应发布此宏(相关讨论见第 5.5 节)。

"h" 宏展开为通过 SMTP HELO 或 EHLO 命令提供给 SMTP 服务器的参数。对于该动词被提供多次的会话,使用最近的实例。

"r" 宏展开为接收 MTA 的名称。这应当是一个完全限定域名,但如果不存在(例如当检查由邮件用户代理 MUA 完成时),或者策略限制另有要求,则应当替换为单词 "unknown"。该域名可能与客户端 MTA 用来定位接收 MTA 的 MX 记录中找到的名称不同。

"t" 宏展开为自 Epoch(1970 年 1 月 1 日午夜,UTC)起近似秒数的十进制表示,在评估时刻的值。这与大多数符合标准的库中的可移植操作系统接口(POSIX)time() 函数返回的值相同。

当宏展开的结果用于域名查询时,如果展开后的域名超过 253 个字符(此格式中域名的最大长度),则左侧会被截断以适配,方法是依次移除域名标签(及其后的点),直到总长度不超过 253 个字符。

大写宏与其小写对应项的展开方式完全相同,然后进行 URL 转义。必须对不在 [RFC3986] 定义的 "unreserved"(非保留)集合中的字符执行 URL 转义。

发送 ADMD 必须注意,使合法邮件的宏展开不超过 DNS 标签 63 字符的限制。特别是,电子邮件地址的 local-part 在两点之间可能超过 63 个字符。

为最小化 DNS 查找资源需求,发送 ADMD 最好避免在任一机制指令中结合使用 "s"、"l"、"o" 或 "h" 宏。尽管这些宏功能强大并允许发布每用户记录,但它们严重限制了实现缓存 check_host() 结果的能力,并降低了 DNS 缓存的有效性。

如果在 check_host() 评估期间处理的任何指令都不包含 "s"、"l"、"o" 或 "h" 宏,则评估结果可以仅基于 <domain> 与 <ip> 进行缓存,缓存时长不超过所涉及 DNS 记录中最短生存时间(TTL)的那个。

7.4. 展开示例

<sender> 为 strong-bad@email.example.com。IPv4 SMTP 客户端 IP 为 192.0.2.3。IPv6 SMTP 客户端 IP 为 2001:db8::cb01。客户端 IP 的 PTR 域名为 mx.example.org。

展开
%{s}strong-bad@email.example.com
%{o}email.example.com
%{d}email.example.com
%{d4}email.example.com
%{d3}email.example.com
%{d2}example.com
%{d1}com
%{dr}com.example.email
%{d2r}example.email
%{l}strong-bad
%{l-}strong.bad
%{lr}strong-bad
%{lr-}bad.strong
%{l1r-}strong
宏字符串展开
%{ir}.%{v}._spf.%{d2}3.2.0.192.in-addr._spf.example.com
%{lr-}.lp._spf.%{d2}bad.strong.lp._spf.example.com
%{lr-}.lp.%{ir}.%{v}._spf.%{d2}bad.strong.lp.3.2.0.192.in-addr._spf.example.com
%{ir}.%{v}.%{l1r-}.lp._spf.%{d2}3.2.0.192.in-addr.strong.lp._spf.example.com
%{d2}.trusted-domains.example.netexample.com.trusted-domains.example.net
IPv6:
%{ir}.%{v}._spf.%{d2}
1.0.b.c.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.8.b.d.0.1.0.0.2.ip6._spf.example.com

8. 结果处理

本节针对 SPF 验证方运营者就 check_host() 对消息的各种可能输出提供指导。SPF 结果的定义见第 2.6 节;本节对每个结果提供了更多细节,用于制定消息处理的本地策略。

每个运行环境都不尽相同。有些接收方适合严格遵循 SPF,对经评估为明确未授权("fail" 以及有时 "softfail")的消息进行确定性处理是常态。另一些接收方则更关注「假阴性」情形。这种担忧通常通过仅在信头中记录结果并允许消息通过以进行额外处理来解决。还有一些接收方将 SPF 作为消息处理决策的若干输入之一。因此,针对任何特定结果并未建立全面的规范性消息处理要求。提供本节是为了呈现每种结果可能成因的完整图景,以及在可用时实验部署期间获得的经验。

基本上有两类处理选择:

o 在试图投递消息的 SMTP 会话内处理,例如返回永久性 SMTP 错误(拒绝)或临时性 SMTP 错误(「稍后重试」);

o 允许消息通过(成功的 SMTP 应答码)并添加额外的头字段以指示 check_host() 返回的结果及其他显著细节;这在第 9 节有更详细的讨论。

8.1. None

对于 "none" 结果,SPF 验证方对客户端是否经授权使用被检查的身份标识毫无信息。check_host() 函数无错误地完成,但未能得出任何结论。

8.2. Neutral

"neutral" 结果表示:尽管发现了该身份标识的策略,但关于客户端没有任何明确的断言(正面或负面)。

"neutral" 结果必须被完全当作 "none" 结果对待;两者之间的区别仅出于信息性目的。比 "none" 更严苛地对待 "neutral" 会打击 ADMD 测试使用 SPF 记录的积极性(见第 10.1 节)。

8.3. Pass

"pass" 结果意味着客户端经授权可以给定身份标识注入邮件。该域现在在信誉意义上可以被认为对发送消息负有责任。进一步的策略检查现在可以在身份标识的合法使用上充满信心地进行。这在附录 G.1 中有进一步讨论。

8.4. Fail

"fail" 结果是一个明确的声明:客户端未获授权在给定的身份标识中使用该域名。对 SPF fail 消息的处置是本地策略的事情。关于制定本地策略的考量见附录 G.2。

如果检查软件选择在 SMTP 事务期间拒收邮件,那么它应当使用 550 的 SMTP 应答码(见 [RFC5321]),并且在支持的情况下使用 5.7.1 增强状态码(见 [RFC3463] 第 3.8 节),外加适当的应答文本。check_host() 函数将返回默认解释字符串,或来自发布 SPF 记录的域的解释字符串(见第 6.2 节)。如果信息并非源自检查软件,最好清楚地表明该文本由发送方域提供。例如:

550 5.7.1 SPF MAIL FROM check failed:
550 5.7.1 The domain example.com explains:
550 5.7.1 Please see http://www.example.com/mailpolicy.html

如果检查软件选择不在 SMTP 事务期间拒收邮件,那么它应当添加一个 Received-SPF 或 Authentication-Results 头字段(见第 9 节),以将此结果传递给下游消息处理器。尽管这对所有 SPF 结果都成立,但对于 "fail" 结果尤为重要,因为该消息明确未获 ADMD 授权。

8.5. Softfail

"softfail" 结果应当被视为介于 "fail" 与 "neutral"/"none" 之间。ADMD 认为该主机未获授权,但不愿做出强策略声明。接收软件不应仅基于此结果拒收消息,但可以使其接受比正常情况更严格的审查。

ADMD 希望阻止该主机的使用,因此希望在发生 "softfail" 结果时获得有限的反馈。例如,收件人的 MUA 可以高亮 "softfail" 状态,或者接收 MTA 可以在首次收到消息时使用灰名单 [RFC6647] 给发送方一条消息,但依据接收方策略在后续尝试时接受它。

8.6. Temperror

"temperror" 结果意味着 SPF 验证方在执行检查时遇到了瞬时(通常是 DNS)错误。检查软件可以选择接受或临时拒绝消息。如果消息因该原因在 SMTP 事务期间被拒绝,软件应当使用 451 的 SMTP 应答码,并且在支持的情况下使用 4.4.3 增强状态码(见 [RFC3463] 第 3.5 节)。这些错误可能由发送方或接收方的 DNS 软件中的问题引起。关于制定本地策略的考量见附录 G.4。

8.7. Permerror

"permerror" 结果意味着该域发布的记录无法被正确解释。这发出了一个明确的错误状况信号,确实需要 DNS 运营方干预才能解决。如果消息因该原因在 SMTP 事务期间被拒绝,软件应当使用 550 的 SMTP 应答码,并且在支持的情况下使用 5.5.2 增强状态码(见 [RFC3463] 第 3.6 节)。请注意,如果 ADMD 使用了宏(第 7 节),此结果有可能是由被检查身份标识具有意外格式所致。也有可能此结果是由某些 SPF 验证方因输入参数具有意外格式而产生(见第 4.8 节)。关于制定本地策略的考量见附录 G.3。

9. 记录结果

为了向 MUA 等下游代理提供它们在评估或表示消息内容表观安全性方面可能需要的信息,建议 SMTP 接收方将 SPF 处理的结果记录在消息信头中。对于选择将 SPF 结果记录在消息信头中供内部过滤器或 MUA 处理的 SPF 验证方运营者,提供了两种方法:第 9.1 节定义了 Received-SPF 字段,它是最初为 SPF 使用定义的 results 字段。第 9.2 节讨论了 Authentication-Results 头字段 [RFC7001],它是较近才指定、设计用于 SPF 及其他认证方法的。

两者都在普遍使用,因此这里都包含。然而,需要注意的是,它们的设计初衷是服务略有不同的目的。Received-SPF 旨在包含足够的信息以能够重建消息的 SPF 评估,而 Authentication-Results 旨在仅传递结果本身以及对终端用户可能有用的相关输出细节(例如,消息的什么属性被实际认证了、以及它包含了什么),将重建工作留给系统日志与 Received 字段内容去处理。此外,Received-SPF 依赖于接收 ADMD 内的代理遵守 [RFC5321] 与 [RFC5322] 的信头字段排序规则,而 Authentication-Results 包含了一些防范不符合规范实现的条款。

一个 SPF 验证方运营者可以选择同时使用两者来服务不同的下游代理。在这种情况下,必须注意确保两个字段传递相同的细节,否则可能出现意外结果。

9.1. Received-SPF 头字段

Received-SPF 头字段是一个跟踪字段(见 [RFC5322] 第 3.6.7 节),应当被前置到现有信头之上,位于 SMTP 接收方生成的 Received: 字段之上。它必须出现在消息中所有其他 Received-SPF 字段之上。该头字段的格式如下:

header-field     = "Received-SPF:" [CFWS] result FWS [comment FWS]
                     [ key-value-list ] CRLF

result           = "pass" / "fail" / "softfail" / "neutral" /
                     "none" / "temperror" / "permerror"

key-value-list   = key-value-pair *( ";" [CFWS] key-value-pair )
                     [ ";" ]

key-value-pair   = key [CFWS] "=" ( dot-atom / quoted-string )

key              = "client-ip" / "envelope-from" / "helo" /
                     "problem" / "receiver" / "identity" /
                      "mechanism" / name

identity         = "mailfrom"   ; for the "MAIL FROM" identity
                     / "helo"     ; for the "HELO" identity
                     / name       ; other identities

dot-atom         = <unquoted word as per [RFC5322]>
quoted-string    = <quoted string as per [RFC5322]>
comment          = <comment string as per [RFC5322]>
CFWS             = <comment or folding white space as per [RFC5322]>
FWS              = <folding white space as per [RFC5322]>
CRLF             = <standard end-of-line token as per [RFC5322]>

该头字段应当在结果之后包含一个 "(...)" 形式的注释,传达支持该结果的信息,例如 <ip>、<sender> 与 <domain>。

以下键值对被设计用于后续的机器解析。SPF 验证方应当提供足够的信息,以便能够验证 SPF 结果——至少包括 "client-ip"、"helo",以及(如果检查了「MAIL FROM」身份标识)"envelope-from"。

client-ip SMTP 客户端的 IP 地址

envelope-from 信封发送方邮箱

helo 在 HELO 或 EHLO 命令中给出的主机名

mechanism 匹配的机制(如果没有机制匹配,则替换以单词 "default")

problem 如果返回了错误,关于该错误的细节

receiver SPF 验证方的主机名

identity 被检查的身份标识;见 <identity> ABNF 规则

其他键可由 SPF 验证方定义。

SPF 验证方必须确保 Received-SPF 头字段不包含无效字符、不会过长(见 [RFC5322] 第 2.1.1 节),并且不包含由发送方提供的恶意数据。

可能生成的各种头字段样式的示例如下:

Received-SPF: pass (mybox.example.org: domain of
   myname@example.com designates 192.0.2.1 as permitted sender)
      receiver=mybox.example.org; client-ip=192.0.2.1;
      envelope-from="myname@example.com"; helo=foo.example.com;

Received-SPF: fail (mybox.example.org: domain of
                    myname@example.com does not designate
                    192.0.2.1 as permitted sender)
                    identity=mailfrom; client-ip=192.0.2.1;
                    envelope-from="myname@example.com";

Received-SPF: pass (mybox.example.org: domain of
   myname@example.com designates 192.0.2.1 as permitted sender)
      receiver=mybox.example.org; client-ip=192.0.2.1;
      mechanism=ip4:192.0.2.1; envelope-from="myname@example.com";
      helo=foo.example.com;

9.2. Authentication-Results 头字段中的 SPF 结果

如第 9 节所述,Authentication-Results 头字段旨在传达边界 MTA 所做的测试列表及其结果。该字段的指定元素提供的信息少于 Received-SPF 字段:

Authentication-Results: myhost.example.org; spf=pass
  smtp.mailfrom=example.net

Received-SPF: pass (myhost.example.org: domain of
   myname@example.com designates 192.0.2.1 as permitted sender)
      receiver=mybox.example.org; client-ip=192.0.2.1;
      envelope-from="myname@example.com"; helo=foo.example.com;

然而,如果愿意,可以在 Authentication-Results 头字段的 "reason" 部分添加 CFWS 并提供等价的信息。

例如,一个展开后的 Authentication-Results 头字段可能如下所示(本例中为「MAIL FROM」检查):

Authentication-Results: myhost.example.org; spf=pass
  reason="client-ip=192.0.2.1; smtp.helo=foo.example.com"
  smtp.mailfrom=user@example.net

10. 对基础设施的影响

本节概述采用本协议将对参与互联网电子邮件的各个实体产生的主要影响。它旨在向读者阐明本协议在何处有意地影响这些实体的运行。本节不是「操作手册」,也不是「最佳实践」文档,更不是一份关于此类实体在本规范下应当做什么的全面清单。

本节仅提供运行建议与说明。它是非规范性的。

[RFC5598] 描述了互联网邮件架构。本节基于该架构的不同段落组织。

10.1. 发送域

希望符合本规范的发起方 ADMD(行政管理域——[RFC5598] 第 2.2.1 与 2.3 节)将需要确定它们允许在向其他 ADMD 中继时使用其域名于「HELO」与「MAIL FROM」身份标识的中继([RFC5598] 第 2.2.2 节)列表。人们认识到,形成这样的列表不仅仅是一个简单的技术活动,还涉及兼具技术与行政管理考量的策略决策。

10.1.1. DNS 资源考量

可以通过选择需要较少 DNS 信息的指令,并将低成本机制放在 SPF 记录中靠前的位置,来最小化 SPF 查找所需的 DNS 资源。

第 4.6.4 节规定了接收方必须使用的限制。发布不超出这些要求的记录至关重要。同样必须仔细权衡合法解决方案的成本与可维护性。

例如,考虑按如下方式设置的域:

example.com.     IN MX   10 mx.example.com.
                  IN MX   20 mx2.example.com.
mx.example.com.  IN A    192.0.2.1
mx2.example.com. IN A    192.0.2.129

假设管理上的意图是授权(pass)mx 与 mx2,同时拒掉其他所有主机。比较以下解决方案:

最佳记录:

example.com.   IN TXT  "v=spf1 ip4:192.0.2.1 ip4:192.0.2.129 -all"

良好记录:

$ORIGIN example.com.
@              IN TXT  "v=spf1 a:authorized-spf.example.com -all"
authorized-spf IN A    192.0.2.1
               IN A    192.0.2.129

较费资源记录:

example.com.   IN TXT  "v=spf1 mx:example.com -all"

浪费的、糟糕记录:

example.com.   IN TXT  "v=spf1 ip4:192.0.2.0/24 mx -all"

10.1.2. 管理员的考量

可能存在行政管理上的考量:使用 "a" 而非 "ip4" 或 "ip6",允许主机轻松地重新编号,代价是每个接收方一次 DNS 查询。使用 "mx" 而非 "a",允许轻松更改邮件主机集合。除非此类变更很常见,否则最好使用资源密集型较低机制,如 "ip4" 和 "ip6" 优于 "a",或 "a" 优于 "mx"。

在一些特定情况下,关于记录内容的标准建议是合适的。为不发送邮件的域发布 SPF 记录是一项公认的最佳实践。不发送邮件的域的记录是:

www.example.com.   IN TXT  "v=spf1 -all"

为单个主机发布 SPF 记录也是最佳实践。主机名通常是 5321.HELO/.EHLO 命令中使用的身份标识。对于具有空 5321.MailFrom 的消息,除了用于基于 5321.HELO/.EHLO 的 SPF 检查外,它还被用作 5321.MailFrom SPF 检查的域。参与邮件处理的单个主机的标准 SPF 记录是:

relay.example.com.   IN TXT  "v=spf1 a -all"

验证正确部署是困难的。[RFC6652] 描述了一种征求 SPF 失败反馈的机制。另一个建议可在附录 C 中找到。

无论使用何种方法,理解 ADMD 的出站邮件架构对于有效部署至关重要。

10.1.3. 退信(Bounces)

如第 2.4 节所述,[RFC5321] 允许 MAIL FROM 为空,这是某些投递状态通知 [RFC3464](通常称为电子邮件退信)的典型特征。在这种情况下,可用于执行 SPF 检查的唯一实体是第 1.1.4 节定义的「HELO」身份标识。管理员确保此身份标识设置正确并拥有适当的 SPF 记录,将增强 SPF 功能。通常将「HELO」身份标识设置为主机名而非域名。大量主机的区域文件生成可以使用 "redirect" 修饰符进行整合,并通过脚本进行初始部署。具体的部署建议在上文第 10.1.2 节给出。

10.2. 接收方

SPF 结果可以与其他方法结合使用,以确定消息最终的本地处置(正面或负面)。它也可以单独被视为决定性的。

试图让一个组织(发送方)指导另一个组织(接收方)的电子邮件处理策略,本质上具有挑战性,并且常常引发争议。正如本文档其他地方所述,并未针对基于 SPF 结果的特定消息处理建立全面的规范性要求。第 8 节与附录 G 中提供的信息供接收方在形成本地处理策略时考量。

主要考量是:SPF 可能对最终有害的邮件(例如,使用一次性域名安排 SPF 通过的垃圾邮件发送者,或来自受信任源的病毒或垃圾邮件爆发)返回 "pass",也可能对最终合法的邮件(例如,经过了邮件别名的合法邮件)返回 "fail"。在制定本地处理策略时,同时考虑这两种情况十分重要。

10.3. 中介

中介是一种用户角色(User Actor)[RFC5598]。也就是说,中介接收消息的「投递」,并发布新消息的「提交」。中介可以使新发布的消息与原始消息相同或不同,随其所愿。示例包括邮件列表(见 [RFC5598] 第 5.3 节)与重发方([RFC5598] 第 5.2 节)。这在 [RFC5321] 第 3.9 节讨论。对于 SPF 的运行,核心关切是新消息的 5321.MailFrom 命令中的电子邮件地址。

由于 SPF 评估基于「最后」一个发送 SMTP 服务器的 IP 地址,将使用中介的地址,而非将消息发送给中介的 SMTP 服务器的地址。一些中介保留来自原始消息的电子邮件地址,而另一些则使用新地址。

如果地址与原始消息相同,且原始消息有关联的 SPF 记录,那么除非使用附录 D 中描述的缓解措施,否则 SPF 评估将失败。

11. 安全考量

11.1. 处理限制

与电子邮件的大多数方面一样,恶意方有若干方式可以利用该协议作为 DoS 攻击的途径。第 4.6.4 节概述的处理限制旨在防止如下类型的攻击:

o 恶意方可以创建一条引用受害者域许多次的 SPF 记录,并向不同的 SPF 验证方发送大量邮件;那些 SPF 验证方随后会制造一次 DoS 攻击。实际上,SPF 验证方被用作放大攻击者的带宽,因为在 SMTP 会话中使用的八位组少于 DNS 查询使用的八位组。使用 SPF 验证方还允许攻击者隐藏攻击的真正来源。这种潜在攻击基于传输大量邮件。

o 尽管 check_host() 的实现应当限制 DNS 查找的数量,但恶意域可能发布超出这些限制的记录,试图在目标处理它们发送的邮件时浪费计算资源。恶意域还可能设计 SPF 记录,导致特定实现使用过多的内存或 CPU,或触发缺陷。如果接收方配置为接受具有 SPF "temperror" 结果的邮件,此类攻击可能导致本应因 SPF "fail" 结果而被拒绝的邮件被接受。这种潜在攻击基于使用特制的 SPF 记录来耗尽受害者的 DNS 资源。

o 恶意方可能向各种合法的邮件主机发送大量自称来自预期目标的邮件。这些合法机器随后在获取相关记录时会向目标呈现 DNS 负载。

o 恶意方理论上可以使用 SPF 记录作为 DNS 查找放大的载体,用于 DoS 攻击。在此场景中,攻击者在自己的 DNS 中发布一条 SPF 记录,该记录使用指向预期受害者的 "a" 与 "mx" 机制,例如 "a:example.com a:foo.example.com a:bar.example.com ...",然后以包含自己域名的 MAIL FROM 值、大批量地向各种目的地分发邮件。任何运行 SPF 验证方的目的地都将开始查询该记录中所有与 "a" 机制关联的名称。记录中使用的名称无需存在,攻击即可奏效。[RFC4408] 发布以来的运行经验表明,让验证方在遇到超过两个「无效查找」(第 4.6.4 节定义)时立即中止处理并返回 "permerror"(第 2.6.7 节),可以以对已有部署基础影响最小的方式缓解此类攻击。

其中,SPF 记录中引用的第三方情形最容易被 DoS 攻击有效利用。因此,对单个邮件服务器而言可能看似合理的限制,仍然可能允许不合理的带宽放大。因此,处理限制需要相当低。

11.2. SPF 授权的邮件可能包含其他虚假身份标识

「MAIL FROM」与「HELO」身份标识的授权并不提供关于消息中使用的其他身份标识的授权/真实性保证。恶意发送方完全有可能在使用 SPF 所用身份标识的消息中注入其自己的域名,并让该域的 SPF 记录授权发送主机,然而消息却可以轻易地在其信头中列出其他身份标识。除非用户或 MUA 注意到底被授权的身份标识与更常被呈现的其他身份标识(如 From: 头字段)不匹配,否则用户可能会被误导,产生错误的安全感。

11.3. 伪造的 DNS 与 IP 数据

该协议有两个方面可能被恶意方利用,以破坏 check_host() 函数的有效性:

o check_host() 的评估严重依赖 DNS。恶意攻击者可以攻击 DNS 基础设施,使 check_host() 看到伪造的 DNS 数据,进而返回错误结果。这可能包括:对于实际域的记录会评估为 "fail" 的 <ip> 值返回 "pass"。DNS 弱点的描述见 [RFC3833],对策见 [RFC4033]。

o 客户端 IP 地址 <ip> 被假定为正确。在现代、正确配置的系统中,这一点不成立的风险为零。

11.4. 跨用户伪造

根据定义,SPF 策略只是将域名映射到一组被授权的 MTA,而不是将整个电子邮件地址映射到一组被授权的用户。尽管 "l" 宏(第 7 节)提供了一种有限的方式,为特定电子邮件地址定义被授权的 MTA 集合,但通常无法通过 SPF 验证同一 MTA 的各个用户对特定电子邮件地址的使用。

防止跨用户伪造取决于邮件服务及其 MTA:基于 SMTP AUTH([RFC4954]),必须将用户限制为只能使用那些确实在其控制之下的电子邮件地址(见 [RFC6409] 第 6.1 节)。验证单个用户身份的另一手段是消息加密,如 Pretty Good Privacy(PGP)([RFC4880])或 S/MIME([RFC5751])。

11.5. 不可信信息源

合规的 SPF 接收方从它接收到的 SMTP 命令以及发送域名持有者发布的 DNS 记录(例如「HELO」域名、信封中的「MAIL FROM」地址,以及域持有者发布的 SPF DNS 记录)中收集信息。这些参数在 SMTP 过程中未被验证。

所有这些信息的片段都由接收方权威范围之外的参与者生成,因此不能保证准确或合法。

11.5.1. 被记录的结果

这些信息通过 Received-SPF: 或 Authentication-Results: 跟踪字段传递给接收方,可以作为 SMTP 拒绝消息返回给客户端 MTA。如果生成了这样的 SMTP 拒绝消息,则必须检查跟踪字段中的信息是否存在无效字符与过长行等问题。

11.5.2. 外部解释

当授权检查失败时,拒绝应答中可能包含解释字符串。发送方与拒绝的接收方都需要意识到,该解释是由被检查 SPF 记录的发布者决定的,一般而言不是接收方决定的。该解释可能包含恶意 URL,或者可能具有攻击性或误导性。

由于 "exp" 修饰符(第 6.2 节)而返回给发送方域的解释,是由域持有者自己发布的发送方策略生成的。只要消息仅以未投递通知([RFC3464])返回给发布自己 DNS SPF 记录中解释字符串的域,受影响的唯一当事方就是该域 SPF 记录的原始发布者。

在实践中,此类未投递通知可能被误投,例如当 MTA 接受一封电子邮件却只在之后才向伪造地址生成通知时,或者当电子邮件转发方未将退信定向回原始发送方时。

11.5.3. 宏展开

宏(第 7 节)允许发送方将任意文本(任何非空的 [US-ASCII] 字符)注入接收方的 DNS 查询。有必要为敌对或意外内容做好准备。

11.6. 隐私暴露

检查 SPF 记录会导致向域所有者发送 DNS 查询。这些 DNS 查询,尤其是如果由 "exists" 机制引起,可能包含关于谁在发送电子邮件以及可能发送给哪个 MTA 的信息。这可能引入一些隐私方面的顾虑,其严重程度或多或少取决于当地法律以及 ADMD 与发件人之间的关系。

11.7. 投递产生 "Fail" 结果的邮件

选择投递 SPF 产生 "fail" 结果的邮件的运营者需要理解,他们正在接纳明确未经声称发送方授权的的内容。尽管存在可被视为「假阴性」的已知失败模式,但接纳那些消息的明确选择会增加最终用户暴露于可能危害的风险。对于通常行为良好的已知良善行为者的域而言尤其如此;来自那些来源的未授权邮件很可能受到更多的怀疑与内容分析。

然而,SPF 并不具备区分良善行为者与恶劣行为者的能力,也不处理已知行为者与未知行为者的概念。这些概念超出了本规范的范围。

12. 汇总的 ABNF

本节是规范性的,与前述文本中的 ABNF 片段有任何出入时,以本语法为准。

ABNF 记法见 [RFC5234]。请注意,根据本 ABNF 定义,字面文本字符串(带引号的那些)是大小写不敏感的。因此,"mx" 匹配 "mx"、"MX"、"mX" 与 "Mx"。

record           = version terms *SP
version          = "v=spf1"

terms            = *( 1*SP ( directive / modifier ) )

directive        = [ qualifier ] mechanism
qualifier        = "+" / "-" / "?" / "~"
mechanism        = ( all / include
                     / a / mx / ptr / ip4 / ip6 / exists )

all              = "all"
include          = "include"  ":" domain-spec
a                = "a"      [ ":" domain-spec ] [ dual-cidr-length ]
mx               = "mx"     [ ":" domain-spec ] [ dual-cidr-length ]
ptr              = "ptr"    [ ":" domain-spec ]
ip4              = "ip4"      ":" ip4-network   [ ip4-cidr-length ]
ip6              = "ip6"      ":" ip6-network   [ ip6-cidr-length ]
exists           = "exists"   ":" domain-spec

modifier         = redirect / explanation / unknown-modifier
redirect         = "redirect" "=" domain-spec
explanation      = "exp" "=" domain-spec
unknown-modifier = name "=" macro-string
                     ; where name is not any known modifier

ip4-cidr-length  = "/" ("0" / %x31-39 0*1DIGIT) ; value range 0-32
ip6-cidr-length  = "/" ("0" / %x31-39 0*2DIGIT) ; value range 0-128
dual-cidr-length = [ ip4-cidr-length ] [ "/" ip6-cidr-length ]

ip4-network      = qnum "." qnum "." qnum "." qnum
qnum             = DIGIT                 ; 0-9
                     / %x31-39 DIGIT       ; 10-99
                     / "1" 2DIGIT          ; 100-199
                     / "2" %x30-34 DIGIT   ; 200-249
                     / "25" %x30-35        ; 250-255
           ; conventional dotted-quad notation, e.g., 192.0.2.0
ip6-network      = <as per Section 2.2 of [RFC4291]>
           ; e.g., 2001:db8::cd30

domain-spec      = macro-string domain-end
domain-end       = ( "." toplabel [ "." ] ) / macro-expand

toplabel         = ( *alphanum ALPHA *alphanum ) /
                   ( 1*alphanum "-" *( alphanum / "-" ) alphanum )
                     ; LDH rule plus additional TLD restrictions
                     ; (see Section 2 of [RFC3696] for background)
alphanum         = ALPHA / DIGIT

explain-string   = *( macro-string / SP )

macro-string     = *( macro-expand / macro-literal )
macro-expand     = ( "%{" macro-letter transformers *delimiter "}" )
                     / "%%" / "%_" / "%-"
macro-literal    = %x21-24 / %x26-7E
                     ; visible characters except "%"
macro-letter     = "s" / "l" / "o" / "d" / "i" / "p" / "h" /
                     "c" / "r" / "t" / "v"
transformers     = *DIGIT [ "r" ]
delimiter        = "." / "-" / "+" / "," / "/" / "_" / "="

name             = ALPHA *( ALPHA / DIGIT / "-" / "_" / "." )

header-field     = "Received-SPF:" [CFWS] result FWS [comment FWS]
                     [ key-value-list ] CRLF

result           = "pass" / "fail" / "softfail" / "neutral" /
                     "none" / "temperror" / "permerror"

key-value-list   = key-value-pair *( ";" [CFWS] key-value-pair )
                     [ ";" ]

key-value-pair   = key [CFWS] "=" ( dot-atom / quoted-string )

key              = "client-ip" / "envelope-from" / "helo" /
                     "problem" / "receiver" / "identity" /
                      "mechanism" / name

identity         = "mailfrom"   ; for the "MAIL FROM" identity
                     / "helo"     ; for the "HELO" identity
                     / name       ; other identities

sender           = Mailbox
ip               = ip4-network / ip6-network
ALPHA            = <A-Z / a-z as per [RFC5234]>
DIGIT            = <0-9 as per [RFC5234]>
SP               = <space character as per [RFC5234]>
dot-atom         = <unquoted word as per [RFC5322]>
quoted-string    = <quoted string as per [RFC5322]>
comment          = <comment string as per [RFC5322]>
CFWS             = <comment or folding white space as per [RFC5322]>
FWS              = <folding white space as per [RFC5322]>
CRLF             = <standard end-of-line token as per [RFC5322]>

13. 贡献者与致谢

本文档很大程度上基于 Meng Weng Wong、Mark Lentczner 与 Wayne Schlitt 的工作。尽管如本节所承认的,许多人参与了本文档,但写作与编辑的很大一部分归功于 Meng、Mark 与 Wayne。

本设计承袭于 Hadmut Danisch 的 [RMX] 与 Gordon Fecyk 的 [DMP]。使用 DNS 记录来检查电子邮件地址合法性的想法,可以追溯到 Paul Vixie [Vixie](基于 Jim Miller 的建议)与 David Green [Green] 在 namedroppers 邮件列表上的更早消息。

Philip Gladstone 向规范贡献了宏的概念,成倍地增加了语言的表达能力,并使每用户与每 IP 查找成为可能。

本文件与 [RFC4408] 的作者还要感谢数以百计的、实际参与了本设计开发工作的个人。他们数量太多,无法一一具名,但包括如下:

SPFbis 工作组的参与者。spf-discuss 邮件列表上的各位。SPAM-L 邮件列表上的各位。IRTF ASRG 邮件列表上的各位。IETF MARID 邮件列表上的各位。#perl 上的各位。

14. IANA 考量

14.1. SPF DNS 记录类型

根据 [RFC4408],IANA 从「域名系统(DNS)参数」注册表中为代码 99 的 SPF RR 类型分配了资源记录类型与 Qtype。该类型的格式与 TXT RR [RFC1035] 相同。记录的字符内容以 [US-ASCII] 编码。

研究表明,RRTYPE 99 没有看到任何实质性使用,事实上它的存在以及 [RFC4408] 中定义的机制已导致一些互操作性问题。因此,它不再适用于 SPF 第 1 版;实现不得使用它。

IANA 已更新「资源记录(RR)TYPEs」注册表,以表明本文件是该 RRTYPE 的参考文档。

14.2. Received-SPF 邮件头字段

根据 [RFC3864],"Received-SPF:" 头字段被添加到 IANA「永久性消息头字段名称」注册表中。以下是注册模板:

Header field name: Received-SPF
Applicable protocol: mail ([RFC5322])
Status: standard
Author/Change controller: IETF
Specification document(s): RFC 7208

14.3. SPF 修饰符注册表

IANA 已将「修饰符名称」注册表(位于发送方策略框架参数之下)中 "exp" 与 "redirect" 修饰符的参考,从 [RFC4408] 更改为本文件。它们的状态保持不变。

15. 参考文献

15.1. 规范性参考文献

[RFC1035] Mockapetris, P., "Domain names - implementation and specification", STD 13, RFC 1035, November 1987.

[RFC1123] Braden, R., "Requirements for Internet Hosts - Application and Support", STD 3, RFC 1123, October 1989.

[RFC2119] Bradner, S., "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.

[RFC3463] Vaudreuil, G., "Enhanced Mail System Status Codes", RFC 3463, January 2003.

[RFC3864] Klyne, G., Nottingham, M., and J. Mogul, "Registration Procedures for Message Header Fields", BCP 90, RFC 3864, September 2004.

[RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter, "Uniform Resource Identifier (URI): Generic Syntax", STD 66, RFC 3986, January 2005.

[RFC4291] Hinden, R. and S. Deering, "IP Version 6 Addressing Architecture", RFC 4291, February 2006.

[RFC5234] Crocker, D. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", STD 68, RFC 5234, January 2008.

[RFC5321] Klensin, J., "Simple Mail Transfer Protocol", RFC 5321, October 2008.

[RFC5322] Resnick, P., Ed., "Internet Message Format", RFC 5322, October 2008.

[RFC5598] Crocker, D., "Internet Mail Architecture", RFC 5598, July 2009.

[RFC5890] Klensin, J., "Internationalized Domain Names for Applications (IDNA): Definitions and Document Framework", RFC 5890, August 2010.

[RFC7001] Kucherawy, M., "Message Header Field for Indicating Message Authentication Status", RFC 7001, September 2013.

[US-ASCII] American National Standards Institute, "USA Code for Information Interchange, X3.4", 1968.

15.2. 资料性参考文献

[BATV] Levine, J., Crocker, D., Silberman, S., and T. Finch, "Bounce Address Tag Validation (BATV)", Work in Progress, May 2008.

[DMP] Fecyk, G., "Designated Mailers Protocol", Work in Progress, May 2004.

[Green] Green, D., "Domain-Authorized SMTP Mail", June 2002.

[RFC1034] Mockapetris, P., "Domain names - concepts and facilities", STD 13, RFC 1034, November 1987.

[RFC1983] Malkin, G., "Internet Users' Glossary", RFC 1983, August 1996.

[RFC2308] Andrews, M., "Negative Caching of DNS Queries (DNS NCACHE)", RFC 2308, March 1998.

[RFC2671] Vixie, P., "Extension Mechanisms for DNS (EDNS0)", RFC 2671, August 1999.

[RFC2782] Gulbrandsen, A., Vixie, P., and L. Esibov, "A DNS RR for specifying the location of services (DNS SRV)", RFC 2782, February 2000.

[RFC3464] Moore, K. and G. Vaudreuil, "An Extensible Message Format for Delivery Status Notifications", RFC 3464, January 2003.

[RFC3696] Klensin, J., "Application Techniques for Checking and Transformation of Names", RFC 3696, February 2004.

[RFC3833] Atkins, D. and R. Austein, "Threat Analysis of the Domain Name System (DNS)", RFC 3833, August 2004.

[RFC3834] Moore, K., "Recommendations for Automatic Responses to Electronic Mail", RFC 3834, August 2004.

[RFC4033] Arends, R., Austein, R., Larson, M., Massey, D., and S. Rose, "DNS Security Introduction and Requirements", RFC 4033, March 2005.

[RFC4408] Wong, M. and W. Schlitt, "Sender Policy Framework (SPF) for Authorizing Use of Domains in E-Mail, Version 1", RFC 4408, April 2006.

[RFC4632] Fuller, V. and T. Li, "Classless Inter-domain Routing (CIDR): The Internet Address Assignment and Aggregation Plan", BCP 122, RFC 4632, August 2006.

[RFC4880] Callas, J., Donnerhacke, L., Finney, H., Shaw, D., and R. Thayer, "OpenPGP Message Format", RFC 4880, November 2007.

[RFC4954] Siemborski, R. and A. Melnikov, "SMTP Service Extension for Authentication", RFC 4954, July 2007.

[RFC5507] IAB, Faltstrom, P., Austein, R., and P. Koch, "Design Choices When Expanding the DNS", RFC 5507, April 2009.

[RFC5751] Ramsdell, B. and S. Turner, "Secure/Multipurpose Internet Mail Extensions (S/MIME) Version 3.2 Message Specification", RFC 5751, January 2010.

[RFC5782] Levine, J., "DNS Blacklists and Whitelists", RFC 5782, February 2010.

[RFC6409] Gellens, R. and J. Klensin, "Message Submission for Mail", STD 72, RFC 6409, November 2011.

[RFC6647] Kucherawy, M. and D. Crocker, "Email Greylisting: An Applicability Statement for SMTP", RFC 6647, June 2012.

[RFC6648] Saint-Andre, P., Crocker, D., and M. Nottingham, "Deprecating the "X-" Prefix and Similar Constructs in Application Protocols", BCP 178, RFC 6648, June 2012.

[RFC6652] Kitterman, S., "Sender Policy Framework (SPF) Authentication Failure Reporting Using the Abuse Reporting Format", RFC 6652, June 2012.

[RFC6686] Kucherawy, M., "Resolution of the Sender Policy Framework (SPF) and Sender ID Experiments", RFC 6686, July 2012.

[RFC6891] Damas, J., Graff, M., and P. Vixie, "Extension Mechanisms for DNS (EDNS(0))", STD 75, RFC 6891, April 2013.

[RMX] Danisch, H., "The RMX DNS RR and method for lightweight SMTP sender authorization", Work in Progress, May 2004.

[Vixie] Vixie, P., "Repudiating MAIL FROM", 2002.

附录 A. 扩展示例

这些示例基于以下 DNS 设置:

; A domain with two mail servers, two hosts, and two servers
; at the domain name
$ORIGIN example.com.
@           MX  10 mail-a
            MX  20 mail-b
            A   192.0.2.10
            A   192.0.2.11
amy         A   192.0.2.65
bob         A   192.0.2.66
mail-a      A   192.0.2.129
mail-b      A   192.0.2.130
www         CNAME example.com.

; A related domain
$ORIGIN example.org.
@           MX  10 mail-c
mail-c      A   192.0.2.140

; The reverse IP for those addresses
$ORIGIN 2.0.192.in-addr.arpa.
10          PTR example.com.
11          PTR example.com.
65          PTR amy.example.com.
66          PTR bob.example.com.
129         PTR mail-a.example.com.
130         PTR mail-b.example.com.
140         PTR mail-c.example.org.

; A rogue reverse IP domain that claims to be
; something it's not
$ORIGIN 0.0.10.in-addr.arpa.
4           PTR bob.example.com.

A.1. 简单示例

这些示例展示了 example.com 各种可能的已发布记录,以及哪些 <ip> 值会导致 check_host() 返回 "pass"。注意 <domain> 是 "example.com"。

v=spf1 +all

   -- any <ip> passes

v=spf1 a -all

   -- hosts 192.0.2.10 and 192.0.2.11 pass

v=spf1 a:example.org -all

   -- no sending hosts pass since example.org has no A records

v=spf1 mx -all

   -- sending hosts 192.0.2.129 and 192.0.2.130 pass

v=spf1 mx:example.org -all

   -- sending host 192.0.2.140 passes

v=spf1 mx mx:example.org -all

   -- sending hosts 192.0.2.129, 192.0.2.130, and 192.0.2.140 pass

v=spf1 mx/30 mx:example.org/30 -all

   -- any sending host in 192.0.2.128/30 or 192.0.2.140/30 passes

v=spf1 ptr -all

   -- sending host 192.0.2.65 passes (reverse DNS is valid and is
      in example.com)

   -- sending host 192.0.2.140 fails (reverse DNS is valid, but not
      in example.com)

   -- sending host 10.0.0.4 fails (reverse IP is not valid)

v=spf1 ip4:192.0.2.128/28 -all

   -- sending host 192.0.2.65 fails

   -- sending host 192.0.2.129 passes

A.2. 多域示例

这些示例展示了相关记录的效果:

example.org: "v=spf1 include:example.com include:example.net -all"

如果来自 example.org 的邮件实际上经由 example.com 与 example.net 的服务器,就会使用此记录。Example.org 指定的服务器是 example.com 与 example.net 指定服务器的并集。

la.example.org: "v=spf1 redirect=example.org"
ny.example.org: "v=spf1 redirect=example.org"
sf.example.org: "v=spf1 redirect=example.org"

这些记录允许一组都使用同一邮件系统的域利用该邮件系统的记录。这样,当邮件设置变更时,只需更新邮件系统的记录。这些域的记录永远无需变更。

A.3. DNS 黑名单(DNSBL)风格示例

想象一下,除了上面列出的域记录外,还有这些(见 [RFC5782]):

$ORIGIN _spf.example.com.
mary.mobile-users                   A 127.0.0.2
fred.mobile-users                   A 127.0.0.2
15.15.168.192.joel.remote-users     A 127.0.0.2
16.15.168.192.joel.remote-users     A 127.0.0.2

以下记录描述了 example.com 上从任意服务器发信、或从其个人服务器发信的用户。

example.com:

v=spf1 mx
       include:mobile-users._spf.%{d}
       include:remote-users._spf.%{d}
       -all

mobile-users._spf.example.com:

v=spf1 exists:%{l1r+}.%{d}

remote-users._spf.example.com:

v=spf1 exists:%{ir}.%{l1r+}.%{d}

A.4. 多重要求示例

假设你的发送方策略要求 IP 地址在某个范围内,且 IP 的反向 DNS 与之匹配。这有多种实现方式,包括以下:

example.com.           SPF  ( "v=spf1 "
                              "-include:ip4._spf.%{d} "
                              "-include:ptr._spf.%{d} "
                              "+all" )
ip4._spf.example.com.  SPF  "v=spf1 -ip4:192.0.2.0/24 +all"
ptr._spf.example.com.  SPF  "v=spf1 -ptr +all"

此示例展示了 "-include" 机制如何有用、以 "+all" 结尾的 SPF 记录如何可能非常严格,以及德·摩根定律(De Morgan's Law)的使用。

附录 B. 相对 RFC 4408 的实现要求变更

相对 [RFC4408] 的实现要求修改,全部要么是 (a) 对 [RFC4408] 中错误的纠正,要么是 (b) 基于 [RFC4408] 发布以来获得的运行经验共识的额外文档说明。

o 已从协议中移除对 DNS RR 类型 SPF(99)的使用;背景见 [RFC6686]。

o 新增了基于「无效查找」的、与 DNS 相关的新处理限制(第 4.6.4 节)。

o 强烈不鼓励使用 ptr 机制与 %p 宏(第 5.5 节与第 7.2 节)。ptr 机制与 %p 宏仍然是协议的一部分,因为它们被发现仍在使用,但应当更新记录以避免使用它们。

o 讨论了使用 "Authentication-Results" 头字段 [RFC7001] 作为 "Received-SPF" 头字段的可能替代方案(第 9.2 节)。

o 对 ABNF 做了一些微小的纠正,使其更清晰、更正确(第 12 节)。SPF 库实现者应当仔细审查修订后的 ABNF,以确定是否需要进行实现变更。

o 移除了 ABNF 中对 X- 字段的使用;背景见 [RFC6648]。

o 记录了关于宏展开后如何处理无效 <domain-spec> 的歧义。必须避免依赖某一种特定行为(第 4.8 节)。

o 基于 [RFC4408] 之后八年的运行经验,更新并扩展了一般性运行信息。见下文第 10 节与附录 D 至 G。

o 审查并更新了安全考量(第 11 节)。

附录 C. 进一步的测试建议

另一种可能有帮助的方法是发布包含 "tracking exists:" 机制的记录。通过查看名称服务器日志,就可以生成一份粗略的列表。例如:

v=spf1 exists:_h.%{h}._l.%{l}._o.%{o}._i.%{i}._spf.%{d} ?all

这一关联的宏展开会将发送 HELO 域、发送电子邮件地址的本地部分、发送电子邮件地址的域部分,以及接收到连接的 IP 地址嵌入到一条 SPF 查询中,并记录在发送方的 DNS 日志里。

这一自 SPF 项目早期就已使用的方法,允许发送方单方面收集数据以评估其 SPF 记录的正确性。与较新的反馈机制不同,它不需要 SPF 验证方的任何特殊合作。作为一个最早发布的 SPF 记录示例,类似的一个至今仍可在 altavista.net 找到。

附录 D. SPF/中介交互

有三个位置可以使用技术来缓解中介造成的非预期 SPF 失败。

D.1. 发起方 ADMD

开始,当邮件首次被发送时:

o 对于可能是转发方的 IP 地址,可以给出 "Neutral" 结果,而非基于已知可靠转发方列表给出 "Fail" 结果。例如:

"v=spf1 mx ?exists:%{ir}.whitelist.example.org -all"

这将导致对 DNS 白名单(DNSWL)的查找,并且仅对既非来自该域 mx 主机(SPF pass)也非来自白名单来源(SPF neutral)的电子邮件产生 "fail" 结果。这实际上将发送方策略的一个要素外包给了白名单的维护者。

o 「MAIL FROM」身份标识可以在本地部分中包含额外信息,以加密方式将邮件标识为来自授权来源。在这种情况下,可以使用如下 SPF 记录:

"v=spf1 mx exists:%{l}._spf_verify.%{d} -all"

然后,可以建立一个专用的 DNS 服务器来服务用于验证本地部分的 _spf_verify 子域。尽管这需要一次额外的 DNS 查找,但这仅在邮件本将因看似不是来自已知良好来源而被拒绝时发生。

注意,由于域名标签 63 字符的限制,这种方法只有在本地部分签名方案保证要么只产生最多 63 个字符的本地部分,要么能够优雅地处理被截断的本地部分时才可靠。保护本地部分的方法是一个本地实现问题;它不必是标准的。其中一种做法的示例见 [BATV]。

o 类似地,可以建立一个专用 DNS 服务器,对来自意外 IP 地址的电子邮件进行速率限制。

"v=spf1 mx exists:%{ir}._spf_rate.%{d} -all"

o SPF 允许为特殊情况创建每用户策略。例如,可以使用以下 SPF 记录与适当的通配符 DNS 记录:

"v=spf1 mx redirect=%{l1r+}._at_.%{o}._spf.%{d}"

D.2. 中介

中间,当邮件被转发时:

o 中介可以通过将「MAIL FROM」重写为它们自己域内的地址来解决问题。这意味着从外部邮箱拒收的邮件将不得不由转发服务转发回原始发送方。存在多种实现此目的的方案,尽管它们在复杂性与对中介的资源需求上差异很大。

o 几个流行的 MTA 可以通过配置一个带有 "owner-" 前缀的额外别名,从「别名」语义强制转为「邮件列表」语义(例如,别名 "friends: george@example.com, fred@example.org" 还需要另一个形如 "owner-friends: localowner" 的别名)。

o 中介可以拒收如果使用 SMTP 应答码 551(User not local,用户不在本地,见 [RFC5321] 第 3.4 节)转发会 "fail" SPF 的邮件,以将正确的目标地址传达给重新发送邮件的一方。

D.3. 接收方 ADMD

结束,当邮件被接收时:

o 如果外部邮箱的所有者希望信任该中介,他可以指示外部邮箱的 MTA 在客户端主机属于该中介时跳过 SPF 测试。

o 可以针对其他身份标识(如「HELO」身份标识)的测试,用来覆盖针对「MAIL FROM」身份标识的失败测试。

o 对于较大的域,可能无法拥有其域邮箱所有者使用的转发服务的完整或准确列表。在这种情况下,可以采用普遍公认的转发服务的白名单。

附录 E. 邮件服务

为第三方域提供邮件服务(例如发送批量邮件)的 MSP(邮件服务提供商——[RFC5598] 第 2.3 节)可能希望根据本文档描述的授权检查调整其配置。如果此类电子邮件使用的「MAIL FROM」身份标识的域名部分使用了某个 MSP 的域,那么该提供商只需确保其发送主机被其自身的 SPF 记录(若有)授权。

如果「MAIL FROM」身份标识未使用 MSP 的域,则必须格外小心。SPF 记录格式为第三方域授权服务提供商代表其发送邮件的 MTA 提供了若干选项。对于拥有大量使用同一 MTA 的客户的 MSP(如 ISP),需要采取措施缓解跨客户伪造的风险(见第 11.4 节)。

附录 F. MTA 中继

中继在 [RFC5598] 第 2.2.2 节描述。授权检查通常排除了在电子邮件消息的发送方与接收方之间使用任意 MTA 中继。

在组织内部,可以有效地部署 MTA 中继。然而,就本文档而言,此类中继实际上是透明的。SPF 授权检查是不同 ADMD 边界 MTA 之间的检查。

对于邮件发送方而言,这意味着已发布的 SPF 记录必须授权任何实际跨互联网发送的 MTA。通常,这些只是边界 MTA,因为内部 MTA 只是将邮件转发给这些 MTA 进行中继。

接收方 ADMD 通常希望在边界 MTA(包括所有次级 MX)处执行授权检查。内部 MTA(包括那些在充当边界 MTA 同时又作为来自次级 MX 的内部中继的 MTA,当它们处理被中继的邮件流时)则不执行授权测试。在边界之外执行授权测试,必须确定首先将消息传输到接收方 ADMD 的主机,而这可能难以从消息信头中提取,因为 (a) 信头字段可能被伪造或格式错误,并且 (b) 没有标准方法编码该信息以使其能被可靠提取。边界之外的测试很可能产生不可靠的结果。这在 [RFC7001] 的附录 D 中有进一步描述。

附录 G. 本地策略考量

SPF 结果可以与其他方法结合使用,以确定消息最终的本地处置(正面或负面)。它也可以单独被视为决定性的。

G.1. SPF Pass 策略

SPF "pass" 结果可以与已知「良善」域的「白名单」结合使用,以绕过部分或所有额外的投递前电子邮件检查。究竟绕过哪些检查以及如何确定适当的白名单条目,必须基于本地条件与要求。

G.2. SPF Fail 策略

SPF "fail" 结果可用于在 SMTP 事务期间基于「MAIL FROM」或「HELO」身份标识结果拒收消息。这降低了各种内容过滤方法的资源需求,并节省了带宽,因为可以在传输 SMTP 内容之前就完成拒收。它还向发送方提供了即时反馈,发送方或许能够由此解决问题。由于本节(附录 G)中描述的一些问题,基于「MAIL FROM」结果拒收邮件时,SPF 式拒收确实存在一定的拒收合法邮件的风险。

SPF "fail" 结果也可以另作他用:作为更大规模评估集合的一个输入,该集合可能基于 SPF "fail" 结果与其他评估技术的组合,导致邮件以某种方式被负面标记(这可能通过投递到特殊的垃圾邮件文件夹、修改主题行,或其他本地确定的方式实现)。制定此类方法的细节必须基于本地条件与要求。以这种方式使用 SPF 结果,不具备 SMTP 拒收所带来的资源节约与向发送方即时反馈的优势,但在设计良好的系统中可能产生较少的不期望拒收。这种方法可能导致未经发送方 ADMD 授权的邮件在终端用户不知情的情况下被投递。

两种通用方法都可以使用,因为它们都对邮件留下了清晰的处置:要么以某种方式投递,要么通知发送方失败。其他处置方式,如「丢弃」或在接受后删除邮件,是不恰当的,因为它们留下了不确定性,并降低了整个互联网上电子邮件的整体可靠性与效用。

G.3. SPF Permerror 策略

"permerror" 结果(见第 2.6.7 节)表示:接收方的 SPF 处理模块确定所检索的 SPF 策略记录无法被解释。这并未就信封中发现的数据的授权使用给出任何真实指示。

与所有结果一样,实现者需要就如何处理产生此结果的消息做出选择。SMTP 只允许少数几个基本选项。

拒收消息是一个选项,因为这是接收方能够为引起注意、同时保护自己免受没有任何确定 SPF 结果的消息影响而做的一件事。然而,如果 SPF 实现有缺陷并返回虚假的 "permerror" 结果,那么只有发送方会被主动通知该缺陷(以被拒收的邮件形式),而使用 SPF 的接收方不会。

侵入性较小的处理选择是投递消息,或许附带某种对遇到困难的标注和/或类似性质的日志记录。然而,这对于希望尽可能严格地实施 SPF 检查的 SPF 验证方运营者而言并不理想,而且这类问题的被动报告通常也无效。

当然也可以选择将此选择交到 SPF 验证方运营者而非实现者手中,因为这类选择往往是本地策略问题,而非具有通用解决方案的状况,但这给本已不简单的环境又增加了一层复杂性。

实现者与 SPF 验证方运营者在处理 SPF 结果时都需要对所有选择与结果保持谨慎。

G.4. SPF Temperror 策略

"temperror" 结果(见第 2.6.6 节)表示:接收方的 SPF 处理模块由于(可能是)瞬时状况而无法检索 SPF 策略记录。这并未就信封中发现的数据的授权使用给出任何真实指示。

与所有结果一样,实现者需要就如何处理产生此结果的消息做出选择。SMTP 只允许少数几个基本选项。

延迟(defer)消息是一个选项,因为这是接收方能够为引起注意、同时保护自己免受没有任何确定 SPF 结果的消息影响而做的一件事。然而,如果 SPF 实现有缺陷并返回虚假的 "temperror" 结果,那么只有发送方会被主动通知该缺陷(以在发送队列超时后被拒收的邮件形式),而使用 SPF 的接收方不会。

由于队列存活时间很长,邮件可能会被反复延迟数天,因此发送方可能对问题的任何意识都可能相当滞后。如果 "temperror" 在多次投递尝试中持续存在,那么将错误视为永久性的、并减少消息在传输中停留的时间,可能是更可取的。

侵入性较小的处理选择是投递消息,或许附带某种对遇到困难的标注和/或类似性质的日志记录。然而,这对于希望尽可能严格地实施 SPF 检查的 SPF 验证方运营者而言并不理想,而且这类问题的被动报告通常也无效。

当然也可以选择将此选择交到 SPF 验证方运营者而非实现者手中,因为这类选择往往是本地策略问题,而非具有通用解决方案的状况,但这给本已不简单的环境又增加了一层复杂性。

实现者与 SPF 验证方运营者在处理 SPF 结果时都需要对所有选择与结果保持谨慎。

作者地址

Scott Kitterman
Kitterman Technical Services
3611 Scheel Dr.
Ellicott City, MD 21042
United States of America
EMail: scott@kitterman.com