摘要

  • draft-gondwana-dkim2-debug-header-01 为 DKIM2 早期测试中的 X-DKIM2-Info 线索规定了共同格式;它没有增加一种新的验证结果。
  • 该字段被刻意排除在哈希与签名之外。它能引导人去取证,却不能证明谁写入了它、动作是否发生、快照是什么,或记录是否完整。

一封失败的邮件看起来像是随身带来了完整案卷。最上方的一行写着入站过滤器实现了哪个 DKIM2 草案、代码来自哪个仓库、由哪个程序执行;下一行称邮件列表软件创建了 Message-Instance m=2,列出参与哈希的头字段,并指向先前保存的副本;最后一行说出站过滤器因链条中断而拒绝签名。

这些细节足以让运维人员迅速缩小排查范围,却不足以让任何系统据此投递、隔离或归责。因为每一行都可以在 DKIM2 所保护的部分继续验证通过时被插入、改写、调换或删除。

这正是 A Diagnostic Header Field for DKIM2 Implementations 划出的界线。其 01 版于太平洋时间 2026 年 9 月 30 日登记;在上海与 UTC 已是 10 月 1 日。它是标记为 I-D Exists、拟定状态为 Informational 的个人 Internet-Draft,不是 DKIM 工作组文件、RFC 或 IANA 注册,也不是 DKIM2 的规范性依赖。正文明确把它定位为早期测试工具,并称其不太可能正式发布。

把实现分歧变成可读线索

DKIM2 试图在邮件列表等中间方修改消息后,仍保留一条可验证的处理链。Message-Instance 携带哈希与 Recipe,用于重建较早状态;DKIM2-Signature 则把受保护的记录串成连续链条。当两个原型对同一消息给出不同结果时,最终的通过或失败往往太粗,无法说明分歧从哪里开始。

X-DKIM2-Info 为这类信息规定了固定位置。每个字段依次包含五个必备标签:draft、repo、date、sw 与 action。draft 指实现采用的 DKIM2 版本;仓库与程序名可区分组件或分支;日期应在该发送方的 DKIM2 行为发生变化时更新。一个字段只记录一个动作,多个动作就写入多个字段。

动作词汇覆盖验证、新建 Message-Instance、签名与拒绝签名。verify=pass 或 verify=fail 后可附自由文本;mi-m=<N> 可给出参与哈希的头字段数量与顺序,并带上读入和存下的快照标识;sign 写明域名与算法;not-signed 则提供实现自定的原因,例如 broken-mi-chain。

01 版还收紧了 00 版的语法:直接复用 DKIM2 的扩展标签格式,要求每个标签末尾都有分号,并将 mi-m<N> 改成 mi-m=<N>。这样,不同实现之间少了一层无意义的拼写差异,更容易暴露真正的行为差异。但语法一致并没有提高证据等级。

字段之所以灵活,正因为证明体系忽略它

DKIM2 基础草案规定,名称以 X- 开头的头字段不进入 Message-Instance 的头哈希。调试草案又要求发送方不得把 X-DKIM2-Info 纳入任何签名或哈希。因此,一个处理方可以把说明行插在它所描述的头字段旁边,而不改变受保护的 Message-Instance,也不会破坏 DKIM2 签名。

同一个性质也让说明行无法证明自己。草案说得很清楚:它不是验证结果,权威结果应放在 Authentication-Results;它只代表发送方声称自己做了什么;软件不得根据它对消息作任何决定。任一经手系统都能在不被发现的情况下增加、修改或删除该字段。

这与 BTW 既有的 DKIM2 Authentication-Results 报道并不重复。前一篇讨论的是消费者如何在同一管理域与 SMTP 事务内,基于本地信任的 authserv-id 接受一份本地判定。X-DKIM2-Info 甚至位于那层判定之下:它从设计上就被排除出自动信任,只负责帮助人提出下一条更准确的问题。

相邻排列不是可验证的因果链

草案要求发送方先加入受保护或操作性的头字段,再把对应调试字段放在它的正上方;合规发送方也不得改动既有的调试字段。这使头部区块呈现出一条容易阅读的时间线。

然而,视觉上的相邻并不等于经过认证的因果关系。后续中间方可以在顶部添加看似可信的 action=sign,把一行移到另一个 Message-Instance 旁,或删掉解释失败原因的那行。仓库路径和程序名都是自我描述,不是二进制证明;行为日期不是提交哈希。这里没有事件 ID、运行实例密钥、序号、接收方回执或完整性声明,无法把多个独立字段变成防篡改日志。

沉默也无法解释。如果最上层 Message-Instance 仍然匹配,发送方可能什么都不增加,格式中便没有动作记录。缺少 mi-m=<N> 不能证明检查已经运行、消息没有变化,或中间方没有删去线索。人只能阅读现有内容,不能由此知道缺少了什么。

快照指针并不携带快照

最具操作价值的两个标签也最能说明边界。snapf 指向计算 Recipe 时使用的较早副本,snaps 指向为下一次比较保存的当前副本。草案强调,这些标识只对写入它的发送方有意义;示例甚至像数据库键或存储路径。

它可以大幅缩短支持沟通:拿着标识去找相应组件的运营者,要求提取字节、日志和留存记录。但离开那个系统后,它不能证明快照内容、保管链、保存期限或当前可用性。除非实现另外给出内容摘要和取回回执,否则复制出来的字符串不是可携带的证据对象。

调试字段还会暴露内部结构。仓库、程序、草案版本、存储布局、参与哈希的头字段清单以及自由文本错误,都可能向外界透露实现细节。运营者可以在出站边界删除字段而不影响 DKIM2 验证;这能减少披露,却也拿走远端测试者需要的线索。因此,内部留存与对外披露必须作为一套策略设计。

解析层面还有一处棱角。RFC 5322 允许头字段名称包含分号,而该格式用分号终止标签,却没有引号机制。草案要求从 hn 中省略不安全名称、限制长度,并替换或删除代入值中的分号。这些规则降低歧义,却不能证明所有早期实现都以同样方式执行。

RFC 6648 对 X- 前缀的普遍风险早有总结:原本私有或实验性的字段会流出边界,逐渐成为事实接口,并给迁移和安全带来含混。此处使用 X- 是清醒的工程权衡,它把调试信息留在 DKIM2 协议意义与密码保护之外。管理者不能把清晰的协议边界误当成已经实现的数据隔离。

让线索带人找到可核验的回执

稳健的调查至少要分开十件事:软件自称的身份、它声称的动作、消息中实际收到的字段、接收或删除它的边界、真实 DKIM2 验证、按本地信任规则产生的 Authentication-Results、对应构建版本与日志、被指向的快照、运营者确认的根因、修复后真实的投递或用户结果。

Heng Lu 的最小初始规范理念支持这种分层。共同格式可以让独立测试更容易,却不应假装能够提供某台机器上的运行事实。运行代码优先则要求继续观察实现、提取快照、重现错误,并验证修复后的结果。

X-DKIM2-Info 的价值,在于把可读指纹留在故障附近。危险只在于人们把支持便利升级成来源证明、政策输入或最终判定。正确的自动化不会根据线索采取处置,而会用线索去找到真正可以验证的证据。

来源