非官方中文译本声明:本页为 IETF RFC 8461《SMTP MTA Strict Transport Security (MTA-STS)》中文译本,由 ztpop.net 整理翻译,仅供学习参考。RFC 文档由 IETF 发布,受 BCP 78 与 IETF 信托法律条款约束;本译本保留原文编号与结构,权威性以英文原文为准。英文原文见 rfc-editor.org/rfc/rfc8461

RFC 8461:SMTP MTA 严格传输安全(MTA-STS)

摘要

SMTP MTA 严格传输安全(MTA-STS)是一种机制,使邮件服务提供商(SP)能够声明其接收传输层安全(TLS)加密的 SMTP 连接的能力,并指定发送 SMTP 服务器是否应当拒绝对那些未提供带有受信任服务器证书的 TLS 的 MX 主机进行投递。

本备忘录的状态

本文是互联网标准跟踪(Standards Track)文档。

本文是互联网工程任务组(IETF)的产物。它代表了 IETF 社区的共识。它经过了公开评审,并已由互联网工程指导组(IESG)批准发布。关于互联网标准的更多信息可在 RFC 7841 第 2 节获取。

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

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

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

目录

1. 引言

SMTP 的 STARTTLS 扩展 [RFC3207] 允许 SMTP 客户端与主机协商使用 TLS 信道进行加密邮件传输。

虽然这种机会式加密协议本身为被动的中间人流量拦截提供了很高的屏障,但任何能够删除 SMTP 会话部分内容(如"250 STARTTLS"响应)或能够重定向整个 SMTP 会话(例如通过覆盖投递域解析所得的 MX 记录)的攻击者,都可以实施降级或拦截攻击。

本文档定义了一种机制,供收件域通过 DNS 与 HTTPS 的组合发布策略,具体指定:

1.1. 术语

本文档中的关键词"MUST(必须)"、"MUST NOT(不得)"、"REQUIRED(要求)"、"SHALL(应)"、"SHALL NOT(不应)"、"SHOULD(应该)"、"SHOULD NOT(不应该)"、"RECOMMENDED(推荐)"、"NOT RECOMMENDED(不建议)"、"MAY(可以)"和"OPTIONAL(可选)",当且仅当它们以所示的全大写形式出现时,应按照 BCP 14 [RFC2119] [RFC8174] 中的描述进行解释。

我们还为本文后续使用定义了以下术语:

2. 相关技术

基于 DNS 的命名实体认证(DANE)TLSA 记录 [RFC7672] 与之类似,因为 DANE 同样旨在将未认证加密或明文传输升级为已认证、抗降级的加密传输。DANE 需要 DNSSEC [RFC4033] 进行认证;而此处描述的机制转而依赖证书颁发机构(CA),且不要求 DNSSEC,代价是面临恶意降级的风险。关于这一权衡的深入讨论,见第 10 节"安全考量"。

此外,MTA-STS 提供一种可选的仅测试模式,使软部署能够检测到策略失败;在 DANE 中可以通过仅为域的部分 MX 部署 TLSA 记录来实现部分部署,但此类机制对 MTA-STS 所使用的按域策略是不可能的。

MTA-STS 的主要动机是提供一种机制,使域即使在部署 DNSSEC 不可取或不切实际时也能确保传输安全。然而,MTA-STS 的设计使其在与 DANE 部署重叠时不会干扰 DANE;特别地,实现了 MTA-STS 验证的发送方 MUST NOT(不得)允许 MTA-STS 策略验证覆盖失败的 DANE 验证。

3. 策略发现

MTA-STS 策略通过 HTTPS 从策略域内一个"well-known(知名)"[RFC5785] 路径提供,其存在与当前版本由策略域处的一条 TXT 记录指示。这些 TXT 记录还包含一个策略"id"字段,使发送 MTA 无需发起 HTTPS 请求即可检查所缓存的策略是否仍然当前。

要发现某个收件域是否实施了 MTA-STS,发送方只需解析一条 TXT 记录。要查看某个发送方已缓存了策略的域是否有可用的更新策略,发送方只需将 TXT 记录的版本"id"与缓存的值进行比对。

3.1. MTA-STS TXT 记录

MTA-STS TXT 记录是在策略域处名为 "_mta-sts" 的一条 TXT 记录。对于域 "example.com",该记录即为 "_mta-sts.example.com"。MTA-STS TXT 记录 MUST(必须)为 US-ASCII,是由分号分隔的键值对,包含以下字段:

一条示例 TXT 记录如下:

_mta-sts.example.com.  IN TXT "v=STSv1; id=20160831085700Z;"

使用 ABNF [RFC7405] 对 "_mta-sts" TXT 记录的形式化定义如下:

   sts-text-record = sts-version 1*(sts-field-delim sts-field)
                     [sts-field-delim]

   sts-field       = sts-id /                 ; 注意 sts-id 记录
                     sts-extension            ; 是必需的。

   sts-field-delim = *WSP ";" *WSP

   sts-version     = %s"v=STSv1"

   sts-id          = %s"id=" 1*32(ALPHA / DIGIT)     ; id=...

   sts-extension   = sts-ext-name "=" sts-ext-value  ; name=value

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

   sts-ext-value   = 1*(%x21-3A / %x3C / %x3E-7E)
                     ; 排除 "="、"、" SP 以及 CTL 的字符

TXT 记录 MUST 以 sts-version 字段开头;其他字段的顺序无关紧要。如果解析器返回了多条 "_mta-sts" 的 TXT 记录,则以 "v=STSv1;" 开头以外的记录将被丢弃。如果最终得到的记录数量不为一条,或者该记录语法无效,发送方 MUST 假定该收件域没有可用的 MTA-STS 策略,并跳过策略发现的剩余步骤。(注意,缺少可用的 TXT 记录本身并不足以移除发送方先前为该策略域缓存的策略,如第 5.1 节"策略应用控制流"所讨论。)如果最终得到的 TXT 记录包含多个字符串,则该记录 MUST 被视为这些字符串被无空格拼接而成。

"_mta-sts" 记录 MAY(可以)返回指向(直接或经由其他 CNAME)某条 TXT 记录的 CNAME,此时发送方 MUST 遵循 CNAME 指针。这可用于策略委托,如第 8.2 节所述。

3.2. MTA-STS 策略

策略本身是一组键值对(类似于 [RFC5322] 中的头字段),由策略主机通过 HTTPS 的 GET 方法从固定的"well-known"[RFC5785] 路径 ".well-known/mta-sts.txt" 提供。策略主机的 DNS 名由在策略域前加上 "mta-sts" 构成。

因此,对于策略域 "example.com",完整 URL 为 "https://mta-sts.example.com/.well-known/mta-sts.txt"。

获取策略时,发送方 SHOULD(应该)校验媒体类型为 "text/plain",以防某些 Web 服务器允许不受信任的用户在用户自定义路径上托管非文本内容(典型如 HTML 或图片)。除 charset=utf-8 或 charset=us-ascii 之外的所有参数均被忽略。额外的 "Content-Type" 参数同样被忽略。

该资源包含以下以 CRLF 分隔的键值对:

一条示例策略如下:

                         version: STSv1
                         mode: enforce
                         mx: mail.example.com
                         mx: *.example.net
                         mx: backupmx.example.com
                         max_age: 604800

使用 ABNF [RFC7405] 对策略资源的形式化定义如下:

sts-policy-record        = sts-policy-field *WSP
                           *(sts-policy-term sts-policy-field *WSP)
                           [sts-policy-term]

sts-policy-field         = sts-policy-version /      ; 必需一次
                           sts-policy-mode    /      ; 必需一次
                           sts-policy-max-age /      ; 必需一次
                           sts-policy-mx /
                           ; 除 mode 为 "none" 的情况外,
                           ; 至少必需一次
                           sts-policy-extension      ; 其他字段

sts-policy-field-delim   = ":" *WSP

sts-policy-version     = sts-policy-version-field sts-policy-field-delim
                         sts-policy-version-value

sts-policy-version-field = %s"version"

sts-policy-version-value = %s"STSv1"

sts-policy-mode          = sts-policy-mode-field sts-policy-field-delim
                           sts-policy-mode-value

sts-policy-mode-field    = %s"mode"

sts-policy-mode-value    =  %s"testing" / %s"enforce" / %s"none"

sts-policy-mx            = sts-policy-mx-field sts-policy-field-delim
                           sts-policy-mx-value

sts-policy-mx-field      = %s"mx"

sts-policy-mx-value      = ["*."] Domain

sts-policy-max-age     = sts-policy-max-age-field sts-policy-field-delim
                         sts-policy-max-age-value

sts-policy-max-age-field = %s"max_age"

sts-policy-max-age-value = 1*10(DIGIT)

sts-policy-extension     = sts-policy-ext-name    ; 附加
                           sts-policy-field-delim ; 扩展
                           sts-policy-ext-value   ; 字段

sts-policy-ext-name      = (sts-policy-alphanum)
                           *31(sta-policy-alphanum / "_" / "-" / ".")

sts-policy-term          = LF / CRLF

sts-policy-ext-value     = sts-policy-vchar
                           [*(%x20 / sts-policy-vchar)
                           sts-policy-vchar]
                           ; 字符,包括 UTF-8 [RFC3629],
                           ; 排除 CTL 且无前导/尾随空格

sts-policy-alphanum     = ALPHA / DIGIT

sts-policy-vchar        = %x21-7E / UTF8-2 / UTF8-3 / UTF8-4

UTF8-2          =   <定义于 [RFC3629] 第 4 节>

UTF8-3          =   <定义于 [RFC3629] 第 4 节>

UTF8-4          =   <定义于 [RFC3629] 第 4 节>

Domain          =   <定义于 [RFC5321] 第 4.1.2 节>

解析器 MUST 接受语法有效的 TXT 记录与策略文件(即,对于 TXT 记录而言为以分号分隔的有效键值对),其中可能包含本文档未指定的额外键值对,此时未知字段 SHALL(应当)被忽略。如果任何非重复字段——即除 "mx" 外的所有字段——被重复,则除第一个条目外的所有条目 SHALL 被忽略。

3.3. HTTPS 策略获取

策略体如上所述由发送 MTA 经 HTTPS [RFC2818] 检索。在为由策略主机获取或更新策略而发起的 TLS 握手中,策略主机 HTTPS 服务器 MUST(必须)呈现一张如下所述对 "mta-sts" DNS-ID [RFC6125](例如 "mta-sts.example.com")有效的 X.509 证书、链接到发送 MTA 所信任的根 CA,且未过期。预期发送 MTA 使用一组与广泛部署的 Web 浏览器和操作系统类似的受信任 CA。关于证书校验的更多细节见 [RFC5280]。

证书对策略主机(即在策略域前加上 "mta-sts")有效,依据 [RFC6125] 中描述的规则,并附带以下应用特定的考量:

该证书 MAY 通过在线证书状态协议(OCSP)[RFC6960]、证书吊销列表(CRL)或其他机制进行吊销检查。

经 HTTPS 获取的策略仅当 HTTP 响应码为 200(OK)时才有效。HTTP 3xx 重定向 MUST NOT(不得)被跟随,且 HTTP 缓存(如 [RFC7234] 所规定)MUST NOT 被使用。

发送方可能希望对获取 HTTPS 端点的尝试频率进行速率限制,即使收件域存在有效的 TXT 记录。在 HTTPS GET 失败的情况下,实现者 SHOULD(应该)将每个版本 ID 的进一步尝试限制为五分钟或更长的时间,以避免以级联失败压垮资源受限的收件方。

发送方 MAY(可以)对 HTTPS GET 施加超时和/或对响应主体的最大大小设限,以避免在尝试策略更新时出现长时间延迟或资源耗尽。建议的超时时间为一分钟,建议的最大策略大小为 64 千字节;策略主机 SHOULD 在该超时与大小限制内以完整的策略主体响应请求。

如果发现了有效的 TXT 记录,但无法通过 HTTPS 获取任何策略(无论出于何种原因),且没有有效的(未过期的)先前缓存策略,发送方 MUST 继续投递,如同该域未实施 MTA-STS。

反之,如果无法通过 DNS 发现或经 HTTPS 获取任何"在用"策略,但发送方缓存中存在有效的(未过期的)策略,发送方 MUST 应用该缓存策略。

最后,为缓解如第 10 节深入讨论的策略刷新被持续干扰的风险,MTA SHOULD 在缓存策略过期之前主动刷新它们;建议的刷新频率为每天一次。为使管理员能够发现策略刷新方面的问题,MTA SHOULD 在此类尝试失败时(通过日志或类似方式)向管理员告警,除非缓存策略的模式为 "none"。

3.4. 智能主机与子域的策略选择

当经由"智能主机(smart host)"——一个管理上配置的中间 SMTP 中继,不同于根据 DNS 确定的邮件收件人服务器——发送邮件时,合规发送方 MUST 将智能主机域视为策略发现与应用的策略域。本规范不提供将策略与采用地址字面量 [RFC5321] 的电子邮件地址相关联的手段。

当向子域处的邮箱发送邮件时,合规发送方 MUST NOT 尝试从父区域获取策略。因此,对于发送至 "user@mail.example.com" 的邮件,策略只能从 "mail.example.com" 获取,而不能从 "example.com" 获取。

4. 策略验证

当向一个发送方拥有有效且未过期的 MTA-STS 策略的域的 MX 发送时,遵从 MTA-STS 的发送 MTA MUST 检查是否:

  1. 策略的至少一个 "mx" 模式匹配所选 MX 主机,如第 4.1 节"MX 主机验证"所述。
  2. 收件邮件服务器支持 STARTTLS,并在 TLS 握手中提供对该主机有效的、基于 PKIX 的 TLS 证书,如第 4.2 节"收件方 MTA 证书验证"所述。

当这些条件未被满足时,称该策略"验证失败"。本节并不规定上述条件未满足时发送 MTA 的行为;发送 MTA 在策略验证失败时的行为描述见第 5 节"策略应用"。

4.1. MX 主机验证

如果 MX 记录名匹配所应用策略中一个或多个 "mx" 字段,则该候选接收 MX 主机依据所应用的 MTA-STS 策略是有效的。匹配与 [RFC6125] 给出的规则一致,限制是通配符字符 '*' 只能用于匹配所呈现标识符中最左侧的完整标签。因此,mx 模式 "*.example.com" 匹配 "mail.example.com",但不匹配 "example.com" 或 "foo.bar.example.com"。

4.2. 收件方 MTA 证书验证

接收 MTA 呈现的证书 MUST 未过期,且 MUST 链接到发送 MTA 所信任的根 CA。证书 MUST 拥有匹配主机名、符合 [RFC6125] 规则、带有 DNS-ID [RFC6125] 的主题备用名(SAN)[RFC5280]。MX 的证书 MAY 还经由 OCSP [RFC6960]、CRL [RFC6818] 或其他机制进行吊销检查。

5. 策略应用

当向一个发送方拥有有效、未过期的 MTA-STS 策略的域的 MX 发送时,遵从 MTA-STS 的发送 MTA 依据策略 "mode" 字段的值,以两种方式之一应用策略验证失败的结果:

  1. "enforce(强制)":在此模式下,发送 MTA MUST NOT 将消息投递给未通过 MX 匹配或证书验证、或不支持 STARTTLS 的主机。
  2. "testing(测试)":在此模式下,同时也实现了 TLSRPT(TLS 上报)规范 [RFC8460] 的发送 MTA 会发送一份指示策略应用失败的报告(只要收件域也实现了 TLSRPT);在任何情况下,消息都可以如同不存在 MTA-STS 验证失败那样被投递。
  3. "none(无)":在此模式下,发送 MTA 应将策略域视为没有活动策略;该模式值的使用见第 8.3 节"移除 MTA-STS"。

当消息由于 "enforce" 策略而投递失败时,合规 MTA MUST NOT 在经由 DNS 检查策略域是否存在更新策略之前永久失败投递消息。(在任何情况下,MTA SHOULD 将此类失败视为瞬时错误并在之后重试投递。)这使得实施域能够即时更新长生命周期策略。

5.1. 策略应用控制流

合规发送方的一个示例控制流由以下步骤组成:

  1. 检查一份缓存策略,其自获取以来的时间未超过其 "max_age"。若不存在,则尝试获取新策略(或许异步进行,以免阻塞消息投递)。可选地,发送 MTA 可在本步无条件检查新策略。
  2. 对于每个候选 MX,按 MX 优先级顺序,尝试投递消息。若存在一个 "enforce" 模式的策略,当尝试向每个候选 MX 投递时,确保第 4 节"策略验证"所述的 STARTTLS 支持与主机身份有效性。若某个候选验证失败,则继续下一个候选(如果存在)。
  3. 在发送方首先经(由 "_mta-sts" TXT 记录中的 "id" 字段所指示的)新策略检查之前,消息投递尝试 MUST NOT(不得)被永久失败。若未发现新策略,则适用于消息临时投递失败情形的既有规则生效(如 [RFC5321] 第 4.5.4.1 节所讨论)。

6. 失败上报

MTA-STS 旨在与 TLSRPT [RFC8460] 配合使用,以确保实施域能够检测到良性与恶意两类失败,并确保指示主动攻击的失败可被察觉。因此,同时也实现了 TLSRPT 的发送方 SHOULD(应该)将以下事件视为可上报的失败:

7. 互操作考量

7.1. SNI 支持

为确保服务器发送正确的证书链,SMTP 客户端 MUST(必须)支持 TLS 服务器名称指示(SNI)扩展 [RFC6066]。当连接 HTTP 服务器以检索 MTA-STS 策略时,SNI 扩展 MUST 包含策略主机的名称(例如 "mta-sts.example.com")。当连接 SMTP 服务器时,SNI 扩展 MUST 包含 MX 主机名。

用于投递 MTA-STS 策略的 HTTP 服务器 MAY(可以)依赖 SNI 来决定向客户端呈现哪条证书链。HTTP 服务器 MUST 以匹配策略主机名的证书链响应,若无法做到则中止 TLS 握手。未发送 SNI 信息的客户端可能看不到预期的证书链。

SMTP 服务器 MAY 依赖 SNI 来决定向客户端呈现哪条证书链。然而,拥有单一身份和单一匹配证书的服务器不需要 SNI 支持。服务器 MUST NOT 强制客户端使用 SNI,因为客户端可能正在使用未认证的机会式 TLS,且可能并不预期服务器的任何特定证书。如果客户端未发送 SNI 扩展,或发送了针对不受支持服务器名的 SNI 扩展,服务器 MUST 仅发送其自行选择的回退证书链。不强制严格匹配所请求的 SNI 主机名的原因是,MTA-STS TLS 客户端通常愿意接受多个服务器名,但只能在 SNI 扩展中发送一个名称。服务器的回退证书可能匹配客户端可接受的另一名称,例如原始的下一跳域。

7.2. 最低 TLS 版本支持

支持 MTA-STS 的 MTA MUST 支持 TLS 1.2 [RFC5246] 或 TLS 1.3 [RFC8446] 或更高版本。应当遵从 [RFC7525] 中的通用 TLS 使用指南。

8. 运维考量

8.1. 策略更新

更新策略要求所有者修改两处:策略域 DNS 区域中的 "_mta-sts" TXT 记录,以及相应的 HTTPS 端点。因此,收件方应当预期,在 HTTPS 与 TXT 两个端点都更新且 TXT 记录的 TTL 已经过之前,策略将继续被发送方使用。

换言之,一个在应用收件方现已过期的策略缓存时无法成功投递消息的发送方,可能要等到 DNS 的 TTL 过后才能发现存在新策略。因此收件方 SHOULD(应该)确保旧策略在此期间继续可用于消息投递,否则将面临消息延迟的风险。

收件方 SHOULD 还在更新 TXT 记录之前更新 HTTPS 策略主体;这一顺序避免了发送方在看到新 TXT 记录时错误地缓存来自 HTTPS 的旧策略的风险。

8.2. 策略委托

域所有者通常将 SMTP 托管委托给不同的组织,例如 ISP 或 Web 主机。在这种情况下,他们可能也希望将 MTA-STS 策略委托给同一组织,这可通过两处修改完成。

首先,策略域必须经由 CNAME 将 "_mta-sts" 记录指向由提供商维护的 "_mta-sts" 记录。这允许提供商控制更新信号。

其次,策略域必须将 "well-known" 策略位置指向提供商。这可以通过将该 "mta-sts" 记录设置为提供商指定的 IP 地址或 CNAME,并向提供商提供对该主机有效的 TLS 证书来实现;或者,也可以为策略域的策略主机设置一个"反向代理"(也称"网关")服务器,配置为从提供商的策略主机提供代理响应。

例如,给定由邮件提供商 "provider.example" 托管的用户域 "user.example",以下配置将允许策略委托:

DNS:

        _mta-sts.user.example.  IN CNAME _mta-sts.provider.example.

策略:

        > GET /.well-known/mta-sts.txt Host: mta-sts.user.example
        < HTTP/1.1 200 OK  # 响应代理来自
                           # https://mta-sts.provider.example 的内容

注意在所有此类情况下,策略端点(本例中为 "https://mta-sts.user.example/.well-known/mta-sts.txt")仍 MUST 呈现对策略主机 "mta-sts.user.example" 有效、而非对提供商域中该主机 "mta-sts.provider.example" 有效的证书。

注意,尽管发送 MTA MUST NOT 在经 HTTPS 获取策略时使用 HTTP 缓存,但此类缓存对按本节所述的反向代理仍然可能有用。一个预期为多个托管域代理的 HTTPS 策略端点——如大型邮件托管提供商或类似者——可能希望指示一个 HTTP Cache-Control "max-age" 响应指令(如 [RFC7234] 所规定)为 60 秒,作为一个合理值,以节省反向代理不必要的高频代理策略获取。

8.3. 移除 MTA-STS

为便于实施策略域干净地选择退出 MTA-STS,并清楚区分指示攻击的失败与指示此类退出的失败,MTA-STS 实现了 "none" 模式,它允许已验证策略权威地指示策略域希望不再实施 MTA-STS,并可能在未来彻底移除 MTA-STS 的 TXT 与策略端点。

实现此类退出的建议工作流如下:

  1. 发布一个新策略,其 "mode" 等于 "none",且 "max_age" 较小(例如一天)。
  2. 发布一条新 TXT 记录以触发获取新策略。
  3. 当所有先前提供过的策略都已过期——通常这是先前已发布策略最后一次被提供的时间加上该策略的 "max_age",但请注意,可能有些比先前发布策略更旧的策略是以比先前发布策略更大的 "max_age" 提供的,从而允许重叠的策略缓存——安全地移除 TXT 记录与 HTTPS 端点。

8.4. 保留 MX 候选遍历

在邮件传输代理中实现发送时 MTA-STS 验证的实现者,应注意修改遍历 MX 候选列表逻辑的风险。由于 MTA-STS 策略可用于预过滤 MX 候选列表中的无效候选,实现一种"两遍"模型很具诱惑力:先根据 MTA-STS 策略过滤可能有效的 MX 候选,然后像没有 MTA-STS 策略那样按序尝试剩余候选。这可能导致不正确的实现,例如消息循环;相反,建议实现者像往常一样遍历 MX 候选列表,并将无效候选视为不可达(即,如同在尝试向该候选投递时出现了某种瞬时错误)。

按普通候选遍历顺序验证 MX 主机的一个后果是,若较高优先级的 MX 通过了 MTA-STS 验证而较低优先级的 MX 未通过,发送方可能永远不会遇到较低优先级的 MX,从而导致仅适用于"备份"MX 的策略错误配置,只有在主 MX 发生故障时才可能被发现的风险。

9. IANA 考量

9.1. 知名 URI 注册表

第 3 节所述的一个新"well-known(知名)"URI 已在"Well-Known URIs"注册表中注册,如下所示:

9.2. MTA-STS TXT 记录字段

IANA 已创建一个名为 "MTA-STS TXT Record Fields" 的新注册表。注册表中的初始条目为:

字段名描述参考
v记录版本RFC 8461 第 3.1 节
id策略实例 IDRFC 8461 第 3.1 节

新字段使用 IANA 的"专家评审(Expert Review)"策略 [RFC8126] 加入此注册表。

9.3. MTA-STS 策略字段

IANA 已创建一个名为 "MTA-STS Policy Fields" 的新注册表。注册表中的初始条目为:

字段名描述参考
version策略版本RFC 8461 第 3.2 节
mode强制行为RFC 8461 第 3.2 节
max_age策略生存期RFC 8461 第 3.2 节
mxMX 身份RFC 8461 第 3.2 节

新字段使用 IANA 的"专家评审"策略加入此注册表。

10. 安全考量

SMTP MTA-STS 试图防范主动攻击者拦截或篡改支持 STARTTLS 的主机之间的邮件。考虑两类攻击:

MTA-STS 仅当发送方能够事先获取并缓存收件域的策略,且攻击者无法获得符合该策略的有效证书时,才能挫败此类攻击。下面我们考察针对此模型的特定攻击。

10.1. 获取签名证书

SMTP MTA-STS 依赖通过基于 PKIX 的 TLS 身份检查 [RFC6125] 进行的证书验证。因此,能够获取目标收件邮件服务有效证书(例如通过攻陷某 CA)的攻击者,能够规避 STS 认证。

10.2. 阻止策略发现

由于 MTA-STS 使用 DNS TXT 记录进行策略发现,能够阻断 DNS 响应的攻击者可以抑制 MTA-STS 策略的发现,使策略域看起来没有 MTA-STS 策略。发送方策略缓存旨在通过降低策略发现的频率,从而缩小脆弱窗口来抵御这种攻击;但这仍是一种风险,能够预测或诱导策略发现的攻击者——例如,通过在进行中间人攻击的同时诱导发送域向一个从未联系过的收件方发送邮件——可能能够挫败策略发现并有效降级消息投递的安全性。

由于这种攻击依赖于拦截初始策略发现,实现者 SHOULD(应该)倾向于尽可能长的策略 "max_age" 值。

由于这种攻击在缓存策略刷新时也可能发生,实现者 SHOULD NOT(不应)等到缓存策略过期才检查更新;如果发送方尝试定期刷新缓存(例如,无论 "_mta-sts" TXT 记录的状态如何,都运行一个每日或每周的后台任务获取当前在用策略,并相应更新其缓存的 "max age"),攻击者将不得不在整个缓存策略的生命周期内持续挫败策略发现,才能阻止一次成功的刷新。

此外,MTA SHOULD 在缓存策略过期之前很久就向管理员告警重复的策略刷新失败(通过告警日志或类似适用机制),使管理员能够检测到这种对策略刷新的持续攻击。(但是,若缓存策略的模式为 "none",则不应实现此类告警,以允许干净地移除 MTA-STS,如第 8.3 节所述。)

对此类降级攻击的抵御——由于即便对于未参与的收件方也能权威地判定"记录的缺失"——是 DANE 的一个特性,因为它使用 DNSSEC 进行策略发现。

10.3. 拒绝服务

我们另外考虑能够修改收件域 DNS 记录的攻击者所造成的拒绝服务风险。在没有 MTA-STS 的情况下,此类攻击者可使发送 MTA 缓存无效的 MX 记录,但仅限于发送方解析器缓存这些记录的时长。有了 MTA-STS,攻击者还可额外通告一个新的、长 "max_age" 的 MTA-STS 策略,其 "mx" 约束验证恶意的 MX 记录,导致发送方缓存该策略,并在受害者重新确保 MX 记录安全后仍然拒绝投递消息。

这种攻击部分通过受害域(在任何时候)发布更新被缓存的恶意策略的新策略来缓解,尽管这确实要求受害域既获取有效的 CA 签名证书,又理解并正确配置 MTA-STS。

类似地,我们考虑那些故意允许不受信任用户在其用户指定子域上提供不受信任内容的域的可能性。在某些情况下(例如 "tumblr.com" 服务),这表现为提供用户注册子域的 HTTPS 托管;在其他情况下(例如动态 DNS 提供商),这表现为允许不受信任用户在提供商域上注册自定义 DNS 记录。

在这些情况下,存在不受信任用户能够在 "mta-sts" 主机上提供自定义内容、包括提供非法的 MTA-STS 策略的风险。我们相信,由于攻击者还需在同一域上提供 "_mta-sts" TXT 记录——据我们所知,这并未广泛提供给不受信任用户——这种攻击变得更加困难。这种攻击还通过上述受害域可在未来任何日期更新无效策略的能力得到了进一步缓解。

10.4. 弱策略约束

即使攻击者无法修改所提供策略,也存在允许同域攻击者接收该域邮件的配置可能。例如,在为 "example.com" 编写 MTA-STS 策略时的一个简便配置选项是设置 "mx" 等于 "*.example.com";在这种情况下,收件域必须考虑这样的风险:任何拥有有效主机名和 CA 签名证书(例如 "dhcp-123.example.com")的用户,从 MTA-STS 策略验证的角度看,都将成为该域的有效 MX 主机。

10.5. Web PKI 系统被攻陷

若干风险适用于用于证书认证的 PKI 系统,既包括 "mta-sts" HTTPS 主机的证书,也包括 SMTP 服务器的证书。这些风险在 Web PKI 生态系统中广泛适用,并非 MTA-STS 所特有;尽管如此,在此语境下仍值得一些考量。

宽泛地说,攻击者可能通过欺诈情形(即冒充受害域的合法所有者)获取证书、攻陷 CA 或委托机构的私钥、获取颁发给受害域的合法证书等方式来攻陷系统,诸如此类。

Web 浏览器常用的方法之一是通过 OCSP [RFC6960] 或 CRL [RFC6818] 撤销被攻陷或欺诈的证书,以帮助缓解其中一些攻击。此类机制本身代表权衡,且未被普遍实现;尽管如此,我们仍建议 MTA-STS 的实现者实现最适用于其实现的撤销机制。

11. 参考文献

11.1. 规范性参考文献

11.2. 资料性参考文献

附录 A. MTA-STS 示例记录与策略

"example.com" 的所有者希望开始使用 MTA-STS,策略将在不影响消息处理方式的情况下向发送方征求报告,以验证处理 "example.com" 邮件的 MX 身份、确认正确使用了 TLS,并确保收件 MX 呈现的证书通过验证。

MTA-STS 策略指示 TXT RR:

       _mta-sts.example.com.  IN TXT "v=STSv1; id=20160831085700Z;"

作为响应主体提供于 "https://mta-sts.example.com/.well-known/mta-sts.txt" 的 MTA-STS 策略文件:

                         version: STSv1
                         mode: testing
                         mx: mx1.example.com
                         mx: mx2.example.com
                         mx: mx.backup-example.com
                         max_age: 1296000

附录 B. 报文投递伪代码

下面是一段伪代码,演示了合规发送 MTA 的逻辑。

尽管此伪代码实现暗示在投递路径中进行同步策略获取,但在实际实现中可能并不可取,我们预计一些实现者反而倾向于在不存在缓存策略时不阻塞投递的后台获取。

   func isEnforce(policy) {
     // 若策略模式为 "enforce" 则返回 true。
   }

   func isNonExpired(policy) {
     // 若策略未过期则返回 true。
   }

   func tryStartTls(connection) {
     // 尝试与 MX 建立 SMTP STARTTLS 连接。
   }

   func certMatches(connection, host) {
     // 假设一个便捷函数,返回 "connection" 中呈现的
     // 服务器证书是否对 "host" 有效。
   }

   func policyMatches(candidate, policy) {
     for mx in policy.mx {
       // 字面量匹配。
       if mx == candidate {
         return true
       }
       // 通配符仅匹配最左侧标签。
       // 通配符之后必须始终跟随一个 '.'。
       if mx[0] == '*' {
         parts = SplitN(candidate, '.', 2)  // 在第一个 '.' 处分割。
         if len(parts) > 1 && parts[1] == mx[2:] {
           return true
         }
       }
     }
     return false
   }

   func tryDeliverMail(connection, message) {
     // 尝试经 "connection" 投递 "message"。
   }

   func tryGetNewPolicy(domain) {
     // 检查 DNS 中 "domain" 的 MTA-STS TXT 记录,并返回
     // 所指示的策略。
   }

   func cachePolicy(domain, policy) {
     // 将 "policy" 存储为 "domain" 的缓存策略。
   }

   func tryGetCachedPolicy(domain) {
     // 返回 "domain" 的缓存策略。
   }

   func reportError(error) {
     // 经 TLSRPT 上报一个错误。
   }

   func tryMxAccordingTo(message, mx, policy) {
     connection := connect(mx)
     if !connection {
       return false  // 无法连接 MX,故这不是 MTA-STS
                     // 错误。
     }
     secure := true
     if !policyMatches(mx, policy) {
       secure = false
       reportError(E_HOST_MISMATCH)
     } else if !tryStartTls(connection) {
       secure = false
       reportError(E_NO_VALID_TLS)
     } else if !certMatches(connection, policy) {
       secure = false
       reportError(E_CERT_MISMATCH)
     }
     if secure || !isEnforce(policy) {
       return tryDeliverMail(connection, message)
     }
     return false
   }

   func tryWithPolicy(message, domain, policy) {
     mxes := getMxForDomain(domain)
     for mx in mxes {
       if tryMxAccordingTo(message, mx, policy) {
         return true
       }
     }
     return false
   }

   func handleMessage(message) {
     domain := ... // 来自收件人 '@' 之后的域部分
     policy := tryGetNewPolicy(domain)
     if policy {
       cachePolicy(domain, policy)
     } else {
       policy = tryGetCachedPolicy(domain)
     }
     if policy {
       return tryWithPolicy(message, domain, policy)
     }
     // 尝试以正常方式(即不带 MTA-STS)投递消息。
   }

贡献者

作者地址