摘要

  • HP-Outer 在加密载荷内记录撰写端主动放到外层的头字段及其值;它证明一次暴露决定,却不假装看见后续每个中继的动作。
  • 接收端必须从实际 MIME 密码层判断保护状态;hp 参数只说明撰写者的意图,不能把“想加密”提升为“已经保密”。
  • 内外 From、签名及地址绑定、传输层认证、界面显示和回复收件人各有不同证据,不能被一个锁形图标压成同一事实。

邮件列表页上的主题是 [...]。点开之后,正文成功解密,界面亮起绿色锁标。很多系统会在这里结束推理:既然正文能解密、主题也被遮住,这封邮件的头部自然“受保护”。

但真正困难的问题还没有开始。外层 From 是运输系统看见并可能检查的地址;内层 From 是密码载荷里的地址。两者若不一致,哪一个应该显示?内层签名所用证书是否真的绑定到那个邮箱?若用户点击回复,哪个地址有权成为收件人?主题是撰写端原本就没放在外层,还是被中途删除后才显得神秘?

RFC 9788 的价值,在于拒绝让“已加密”替这些问题代答。它把邮件头保护拆成可以核对的分层凭证。

密码保护首先是一棵 MIME 树

RFC 9787 从消息结构出发。S/MIME 与 PGP/MIME 的签名层提供完整性与认证,密码层提供保密性;具体完整性还取决于封装形式。层的顺序有意义:先签名再加密,与先加密再签名,不是同一个证据结构。

从最外层 MIME 类型开始连续相接的密码层,构成“密码信封”。内部第一块非密码内容才是密码载荷。如果中间被普通 MIME 部分隔断,更深处的签名或密文不能自动替整封邮件背书。收到的结构是事实,图标只是对事实的一次呈现。

传统加密邮件常保护正文,却把相当多的头字段留在外面。攻击者可能替换主题或表面作者,而正文密文仍能正常解开。RFC 8551 曾用一层 message/rfc822 包住完整消息;一些旧客户端在显示和安全处理上出现问题。RFC 9788 改为把撰写端知道的头字段直接复制进密码载荷。

对加密邮件而言,非结构性头字段有三种外层命运:原样保留、改成遮蔽值,或者移除。密文中存在一份副本,并不能说明外层当时选择了哪一种。

HP-Outer 是撰写动作的回执

新字段 HP-Outer 位于加密载荷内部。撰写端每主动暴露一个外层非结构性头字段,都必须在这里留下字段名和外层值的受保护副本。

若外层 Subject 被改为 [...],回执保存这个遮蔽值;若 Date 原样公开,回执保存明文日期;若某个受保护字段完全没有对应的 HP-Outer,撰写端是在声明:注入邮件系统时,它没有主动把该字段放到外层。

这份回执的权威有明确边界。它与载荷共享密码保证,因此能证明撰写端记录的选择。它不能证明中继之后没有添加、删除或修改字段,也不能证明外界无法从 SMTP 投递对象、收件人密钥标识、Received、邮箱排序或时间关联中推断相同信息。

这个边界正是防错机制。假设撰写端把 Cc 原样放在外层,也在内层留有副本;攻击者中途删掉外层 Cc。只比较最终内外差异的客户端,会误以为 Cc 一直被隐藏。匹配的 HP-Outer 则表明撰写端曾主动公开它。界面不能把这个字段描述为历史上“已加密”;但生成回复时,客户端仍可按更保守的方式避免把它重新泄露到明文。

一张回执只证明它观察到的动作。它不拥有路径,也不拥有结果。这正是现实分层的纪律。

策略名称不上线,决策结果才上线

RFC 9788 把外层处理规则称为“头部保密策略”HCP。它接收字段名和原始值,输出原值、遮蔽后的值,或 null——即从外层删除。

推荐的 hcp_baseline 很克制:外层 Subject 变成 [...],Comments 与 Keywords 被移除,其余字段透传。hcp_shy 进一步去掉 From、To、Cc 中的人类可读显示名,并把 Date 转为 UTC。后者少泄露一些可读元数据,却需要更精确的解析,也可能带来投递、显示和可用性成本,因此不是默认推荐。

HCP 的名称本身不会随邮件传输。接收端通过 HP-Outer 看见策略造成的具体决定。IANA 注册表保存可实现的稳定描述与推荐状态,却不证明某个客户端已经实现、某个账户已启用,或某封邮件确实采用。

这是一种薄公共层设计:共同层只规定语法、可复现的函数和注册条件。更激进的策略可以在本地自愿采用,但要承担本地验证。隐藏 To、Cc、References 或 In-Reply-To 也许减少泄露,却可能影响过滤、线程、列表和排障。注册表不能替运行代码支付这些代价。

hp=cipher 只表达意图

受保护 Content-Type 上的 hp 参数说明撰写者原本想做什么:cipher 表示计划以加密方式保护头字段,clear 表示仅签名。它不是实际密码状态的快捷开关。

一封只有签名层、却写着 hp=cipher 的邮件,仍然没有保密性。接收端不得用意图抬高结论。反过来,中间人也可能给原本只签名的邮件再包一层加密。收件人看到的密码层是真实收到的结构,却不代表原作者创建了它。

当意图和信封结构不一致时,缺少 HP-Outer 不能被解释为“原作者把所有字段都隐藏了”。审计必须分别保存 MIME 树及层序、hp、全部 HP-Outer、最终外层字段和解析器输出。单一的 encrypted=true 会销毁因果链。

显示谁,与回复谁,不是同一道题

现代邮件系统可能在传输层检查外层 From,端到端签名则保护内层 From。RFC 9788 把内外地址的 addr-spec 不一致称为 From mismatch。

受保护不等于身份已验证。内层签名必须有效,而且证书必须正确绑定到内层邮箱。若地址不一致,同时缺少有效绑定,客户端应给出接近钓鱼警告的提示,并展示两个地址。

依赖传输系统认证的客户端,此时应把实际外层 From 作为谨慎显示值。否则恶意撰写者只要把名人地址塞进受保护区域,就能制造新的冒认界面。

然而生成回复时,收件人只能来自受保护字段。若让可被中间人修改的外层地址控制回复,攻击者就能把一段加密对话诱导到自己邮箱。

这不是协议自相矛盾。显示在防止虚假身份,回复在防止路径劫持,两者消费不同证据。人类显示名又是第三层:它没有邮箱地址那样的全局唯一性,也未必被证书绑定。字体放大不会增加密码权威。

隐私必须说明对谁隐私

一个字段同时存在于内外层,就没有对传输代理保密;它即使从外层移除,也会向所有预期收件人公开。收件人密钥、SMTP 信封、Received 和时间仍可泄露关系。User-Agent、带模式的 Message-ID 或错误放入的 Bcc,可能把不该给收件人的信息忠实加密并交付。

邮件列表和中继可以在撰写后添加新字段。这些字段靠近密文,却不继承端到端保护。回复与转发还可能把已解密的 Subject、References 或正文引文复制进明文消息。

因此,结论不是“邮件头加密无效”。恰恰相反:RFC 9788 的改进来自缩小每个断言的范围。字段、观察者、身份和动作各自留据,安全才有可能被验证。

来源

  1. RFC 9788 — Header Protection for Cryptographically Protected Email
  2. RFC 9787 — Guidance on End-to-End Email Security
  3. RFC 8551 — S/MIME 4.0 Message Specification
  4. RFC 5322 — Internet Message Format
  5. RFC 3156 — MIME Security with OpenPGP
  6. RFC 7489 — Domain-based Message Authentication, Reporting, and Conformance
  7. IANA — Mail Header Confidentiality Policies
  8. IANA — Message Headers
  9. Heng Lu — 运行代码优先
  10. Heng Lu — 最小初始规范与本地化未来决策
  11. Heng Lu — 现实层与符号权力