RFC 3207《SMTP 服务扩展:基于 TLS 的安全 SMTP》中文导读
非官方中文导读声明:本页为 IETF RFC 3207《SMTP Service Extension for Secure SMTP over Transport Layer Security》 的非官方中文技术导读,按原文章节顺序梳理规范要点,并非逐字全文翻译;任何规范性判断均以英文原文为准。英文原文见 rfc-editor.org/rfc/rfc3207.txt。
1. 这份文档解决什么问题
SMTP 在设计之初以明文传输为前提,信封、信头与信体在链路上完全可见,任何具备链路观察能力的中间人都能读取甚至篡改邮件。RFC 3207 给 SMTP 增加了一个服务扩展,让客户端与服务端可以在既有的明文连接上就地升级为 TLS 保护的连接,从而为这一段传输提供机密性,并在双方使用证书时提供身份认证能力。
它废止了更早的 RFC 2487,是今天几乎所有 MTA 之间 STARTTLS 行为的规范依据。需要强调的是,本扩展保护的是相邻两跳之间的链路,而不是端到端的邮件内容。
2. 扩展的注册要素
该扩展在 SMTP 服务扩展框架下的注册信息很简洁:服务名为 STARTTLS,EHLO 关键字同为 STARTTLS 且不带任何参数,新增一个动词 STARTTLS,并且不为任何既有命令增加参数。
C: EHLO client.example.org S: 250-server.example.net Hello S: 250-STARTTLS S: 250 HELP
服务端在 EHLO 响应中通告 STARTTLS,即表示它当前具备协商 TLS 的能力。
3. STARTTLS 命令与响应码
命令本身没有参数。服务端对 STARTTLS 的应答限定为三种:
220——准备开始 TLS 协商;501——语法错误(本命令不允许带参数);454——因暂时性原因当前无法提供 TLS。
收到 454 时,客户端需要依据本地策略决定后续动作:若 TLS 原本用于客户端认证,也许可以继续尝试;若 TLS 用于加密,则要在“明文发送”“稍后重试”“放弃并向发件人告警”之间做取舍。
有两条容易被忽略的硬性约束。其一,公开引用的 SMTP 服务器不得把 STARTTLS 作为投递本域邮件的强制前提——这里“公开引用”指运行在 25 端口、并出现在某域 MX(或缺 MX 时的 A)记录中的服务器;这条规则的目的是防止 TLS 扩展破坏互联网 SMTP 基础设施的互通性。其二,若客户端使用了流水线(pipelining),STARTTLS 必须是命令分组中的最后一条。
非公开引用的服务器(例如仅服务内部或提交场景的服务器)则可以要求先完成 TLS 协商,此时对除 NOOP、EHLO、STARTTLS、QUIT 之外的命令应当返回 530(启用增强状态码时为 5.7.0)。
4. 握手后的状态重置
这是实现上最容易出错的一段。TLS 握手完成后,双方必须立即根据实际达成的认证与机密性水平判断是否继续会话:客户端若认为强度不足,应当立刻发送 QUIT;服务端若认为不足,则应当对除 QUIT 外的所有命令回 554。
更关键的是状态丢弃规则:TLS 握手成功后,服务端必须丢弃此前从客户端获得的一切协议状态与知识(如同刚建立新连接),客户端也必须丢弃此前的 EHLO 结果;客户端随后必须重新发出 EHLO。这条规则直接决定了实现是否会受“明文阶段注入命令”类攻击影响。
C: STARTTLS S: 220 Go ahead C:C: EHLO client.example.org <- 必须重新发送 S: 250-server.example.net Hello S: 250 AUTH ...
5. 安全考量:降级攻击是主要威胁
原文的安全考量部分明确指出,SMTP 不是端到端机制。一封邮件可能经过两跳以上,为其中一对服务器加上 TLS,并不意味着整条链路都受到保护;能够认证 SMTP 客户端,也不等于该客户端收到这封邮件时它就是可信的。
最直接的攻击是剥离通告:中间人删除服务端响应里的“250 STARTTLS”行,客户端便不会尝试升级;变体是允许通告、但篡改客户端的 STARTTLS 请求与服务端的回应。为对抗这类攻击,规范要求客户端与服务端都必须支持一种配置能力——对选定的对端强制要求成功协商出合适的密码套件后才允许传输邮件;同时也应当提供“尽力而为”的可选模式。实现可以记录某对端曾经使用过 TLS,并在后续会话未用 TLS 时告警。
换句话说:机会性 TLS 只能防被动监听,要防主动中间人,必须叠加强制策略。这也是后来 MTA-STS 与 DANE 出现的直接动因。
参考:https://www.rfc-editor.org/rfc/rfc3207.txt
