DKIM 验签失败怎么系统性定位?该从签名头的哪些标签查起?

1 DKIM 验签失败怎么系统性定位?该从签名头的哪些标签查起?
先取签名头,按标签建立坐标

RFC 6376 第 3.5 节(The DKIM-Signature Header Field)定义了签名头的全部标签。排障时先把这几个取出来,后续每一步都围绕它们展开:

  • d= 签名域、s= 选择器 —— 决定去哪里取公钥
  • a= 算法 —— 决定用什么算法验证
  • c= 规范化 —— 决定哈希前如何整理
  • h= 头字段列表 —— 决定哪些头被签名覆盖
  • bh= 正文哈希、b= 签名值 —— 两个失败点相互独立的校验对象。
  • t= 签名时间、x= 过期时间 —— 决定签名是否仍在有效期内
第一步:能不能取到公钥

s=d= 拼出密钥记录名 <s>._domainkey.<d>,直接查询。三种结果对应三类问题:

  • 查不到记录:选择器写错、记录未发布,或轮换时旧选择器已被删除而仍有旧签名邮件在途。
  • 查到但 p 为空:按第 3.6.1 节(Textual Representation),空的 p 表示该公钥已被撤销,签名将被判定无效。确认是否为有意撤销。
  • 查到且 p 有值:进入下一步。此时应逐字符比对 DNS 返回的公钥与签名系统持有的公钥——录入时被插入换行或空格是高频故障。
第二步:区分 bh 失败与 b 失败(最关键的一步)

验证包含两个独立环节:先校验正文哈希 bh=,再校验整体签名 b=。分清是哪一个失败,能立刻把排查范围缩小一半:

  • bh 不匹配 → 正文被改动。常见于中转追加内容、编码转换、MIME 重组、行尾处理差异。此时无需再怀疑密钥问题。
  • bh 匹配但 b 校验失败 → 问题在头部或密钥。可能是被签名覆盖的头字段遭改写、规范化选择不当,或公钥与私钥不配对。

这一分叉是 DKIM 排障的主干,务必优先确定,避免在错误方向上浪费时间。

第三步:核对算法与规范化
  • 算法(a=:RFC 6376 第 3.3 节(Signing and Verification Algorithms)定义了签名与验证算法。需确认签名方所用算法为验证方所支持,且与密钥记录中声明的算法一致。算法不匹配会直接导致验证失败。
  • 密钥长度:过短的密钥可能被验证方以强度不足为由拒绝。轮换时应一并评估。
  • 规范化(c=:若使用了 simple,对空白与折行零容忍,失败率显著偏高。改为 relaxed/relaxed 是最常见且有效的修复动作。
第四步:检查覆盖范围与有效期
  • h= 列表是否过宽。把大量易变头字段签进去,等于主动扩大失败面。收敛到必要字段。
  • h= 中是否包含实际不存在的头字段。这在部分实现中用于防止头字段被后续添加,但配置不当会引发意外失败。
  • x= 是否已过期。若设置了过期时间且邮件在队列中长时间重试,到达时签名可能已过期。过期时间不宜设置过短,需覆盖最长在途时间。
  • t= 时间是否异常。签名系统时钟偏差过大可能引发时间相关判定问题。
第五步:定位到具体环节
  1. 确认失败是否普遍。若仅对部分接收方失败,优先怀疑 DNS 传播不一致或对方策略差异;若对所有接收方失败,则是签名侧配置问题。
  2. 确认失败是否只在特定路径出现。若直投正常、经转发或列表后失败,属于中介修改邮件所致,应从链路层面解决而非调签名参数。
  3. 确认是否与轮换时点重合。时间上高度吻合的失败,几乎都与密钥轮换步骤顺序有关。
  4. 用聚合报告交叉验证。报告中的 DKIM 结果与签名域信息,能帮助确认是哪一套发送系统在出问题。

参考:RFC 6376 DomainKeys Identified Mail (DKIM) Signatures