DKIM 验证:电子邮件认证最佳实践
鸟
2017年4月8日
电子邮件
1 min read

关键要点
DKIM (DomainKeys Identified Mail) 是一种基于内容的电子邮件认证方法,用于验证邮件在签名后是否被更改。
与 SPF 验证发送路径不同,DKIM 使用加密签名验证邮件内容本身。
DKIM 使用两个密钥:一个用于签署外发邮件的私钥和一个发布在 DNS 中供接收者验证签名的公钥。
DKIM 签名包括邮件头和正文的哈希值、选择器、时间戳和身份字段,所有这些接收者都必须再现和验证。
DKIM 验证依赖于先接收完整的消息,这意味着故障可能在过程的后期发生。
故障排除失败的 DKIM 验证通常很困难,因为发送者和接收者无法重现彼此的签名/验证环境。
即使 DKIM 失败,也通常不会自己直接导致收件箱问题,除非与其他负面声誉信号结合。
Q&A 精华
DKIM 实际上有什么作用?
DKIM 将加密签名附加到电子邮件上,使接收服务器能够确认消息内容在发送后是否被修改。
DKIM与SPF有何不同?
SPF 验证 谁 被允许为一个域发送邮件(基于路径)。
DKIM 验证 内容 是否完整(基于内容)。
两者目的不同,但相辅相成。
DKIM-Signature 标头内部是什么?
关键字段包括:
v= 版本
a= 算法
c= 标准化规则
d= 签名域
s= 选择器
h= 包含在签名中的头部
bh= 正文散列
b= 实际签名数据
每个部分都对签名验证有贡献。
接收服务器如何验证DKIM?
提取 d= 和 s= 值。
在以下位置查找公钥:
selector._domainkey.domain
重新生成标题和正文的哈希值。
将它们与电子邮件中的 bh= 和 b= 值进行比较。
如果匹配,消息通过 DKIM。
是什么原因导致DKIM失败?
消息在传输中被更改(如果使用严格的规范化,甚至空白也会发生变化)
DNS中公钥不正确或缺失
DNS格式错误
选择器被移除或错误旋转
身份不匹配(i=必须与d=相同域或子域)
为什么 DKIM failures 可能会很难 troubleshoot?
因为签署者和验证者在完全不同的环境中运行——双方都无法重建对方的哈希条件或签名状态。
这使得不透明的“哈希不匹配”故障成为最令人生气的诊断问题。
DKIM 失败是否意味着电子邮件会进入垃圾邮件?
不一定。
DKIM 只是一个信号。如果域名声誉良好并通过其他检查(SPF、DMARC 对齐、低投诉率),单独的 DKIM 失败通常不会对 Inbox 产生影响。
为什么要使用 DKIM?
保护信息的完整性
建立域名声誉
实现DMARC对齐
帮助邮箱提供商区分合法发件人与欺骗者
DKIM被视为所有严肃电子邮件发件人的最佳实践。
当我们谈论“邮件认证”时,我们指的是一种向邮件接收者提供一定程度确信的方法,即邮件确实来自所声称来源的技术。这类技术背后的理念是建立起某种程度的防御,以防止欺诈性邮件,例如钓鱼和欺骗邮件,这些邮件可能削弱接收者对接收邮件的信任。不过,发送经过认证的邮件并不意味着该邮件是好的或受欢迎的;它仅仅意味着可以可靠地建立经过认证方的声誉,并在邮件接受和放置决策中使用。
目前,使用中有两种形式的邮件认证:
发送者政策框架 (SPF)
域名密钥识别邮件 (DKIM)
在今天的文章中,我将介绍DKIM是什么以及它如何运作。
当我们谈论“邮件认证”时,我们指的是一种向邮件接收者提供一定程度确信的方法,即邮件确实来自所声称来源的技术。这类技术背后的理念是建立起某种程度的防御,以防止欺诈性邮件,例如钓鱼和欺骗邮件,这些邮件可能削弱接收者对接收邮件的信任。不过,发送经过认证的邮件并不意味着该邮件是好的或受欢迎的;它仅仅意味着可以可靠地建立经过认证方的声誉,并在邮件接受和放置决策中使用。
目前,使用中有两种形式的邮件认证:
发送者政策框架 (SPF)
域名密钥识别邮件 (DKIM)
在今天的文章中,我将介绍DKIM是什么以及它如何运作。
当我们谈论“邮件认证”时,我们指的是一种向邮件接收者提供一定程度确信的方法,即邮件确实来自所声称来源的技术。这类技术背后的理念是建立起某种程度的防御,以防止欺诈性邮件,例如钓鱼和欺骗邮件,这些邮件可能削弱接收者对接收邮件的信任。不过,发送经过认证的邮件并不意味着该邮件是好的或受欢迎的;它仅仅意味着可以可靠地建立经过认证方的声誉,并在邮件接受和放置决策中使用。
目前,使用中有两种形式的邮件认证:
发送者政策框架 (SPF)
域名密钥识别邮件 (DKIM)
在今天的文章中,我将介绍DKIM是什么以及它如何运作。
DKIM 概览
与其身份验证对应的 SPF 不同,SPF 提供了一种方法让域授权主机代表其发送邮件,DKIM 提供了一种方法,使实体(域、组织、个人等)能够对消息负责,而与实际发送消息的实体无关。虽然在许多情况下,负责实体和发送实体会是相同的,或至少是密切相关的,但在 DKIM 中,并不要求必须如此。
我希望通过这篇文章,你能够学习并理解以下关于 DKIM 的概念:
DKIM 是一种“基于内容”的身份验证,不同于“基于路径”的 SPF。
负责实体通过在消息头中插入一对加密哈希来“签署”消息,以此申明其责任。
DKIM 验证由接收域尝试生成相同的两个哈希来完成。
在许多情况下,DKIM 验证在发送服务器传输完整消息之前无法完成。
验证失败可能难以排查。
与其身份验证对应的 SPF 不同,SPF 提供了一种方法让域授权主机代表其发送邮件,DKIM 提供了一种方法,使实体(域、组织、个人等)能够对消息负责,而与实际发送消息的实体无关。虽然在许多情况下,负责实体和发送实体会是相同的,或至少是密切相关的,但在 DKIM 中,并不要求必须如此。
我希望通过这篇文章,你能够学习并理解以下关于 DKIM 的概念:
DKIM 是一种“基于内容”的身份验证,不同于“基于路径”的 SPF。
负责实体通过在消息头中插入一对加密哈希来“签署”消息,以此申明其责任。
DKIM 验证由接收域尝试生成相同的两个哈希来完成。
在许多情况下,DKIM 验证在发送服务器传输完整消息之前无法完成。
验证失败可能难以排查。
与其身份验证对应的 SPF 不同,SPF 提供了一种方法让域授权主机代表其发送邮件,DKIM 提供了一种方法,使实体(域、组织、个人等)能够对消息负责,而与实际发送消息的实体无关。虽然在许多情况下,负责实体和发送实体会是相同的,或至少是密切相关的,但在 DKIM 中,并不要求必须如此。
我希望通过这篇文章,你能够学习并理解以下关于 DKIM 的概念:
DKIM 是一种“基于内容”的身份验证,不同于“基于路径”的 SPF。
负责实体通过在消息头中插入一对加密哈希来“签署”消息,以此申明其责任。
DKIM 验证由接收域尝试生成相同的两个哈希来完成。
在许多情况下,DKIM 验证在发送服务器传输完整消息之前无法完成。
验证失败可能难以排查。
“Content-Based” Authentication
DKIM 被称为“基于内容”的认证,而不是“基于路径”,因为消息是否通过 DKIM 验证完全取决于内容在签署和尝试验证之间是否发生了变化。
DKIM 被称为“基于内容”的认证,而不是“基于路径”,因为消息是否通过 DKIM 验证完全取决于内容在签署和尝试验证之间是否发生了变化。
DKIM 被称为“基于内容”的认证,而不是“基于路径”,因为消息是否通过 DKIM 验证完全取决于内容在签署和尝试验证之间是否发生了变化。
DKIM Signing 和 Validation
希望进行DKIM签名邮件的组织将首先生成两个加密密钥。其中一个密钥保持私密,并可供发送服务器用于邮件的签名,另一个则公开在DNS中,供接收域尝试验证签名。生成这些密钥和安装它们的方法依赖于平台,超出了本文的范围,虽然稍后我将描述在DNS中发布公用DKIM密钥。
希望进行DKIM签名邮件的组织将首先生成两个加密密钥。其中一个密钥保持私密,并可供发送服务器用于邮件的签名,另一个则公开在DNS中,供接收域尝试验证签名。生成这些密钥和安装它们的方法依赖于平台,超出了本文的范围,虽然稍后我将描述在DNS中发布公用DKIM密钥。
希望进行DKIM签名邮件的组织将首先生成两个加密密钥。其中一个密钥保持私密,并可供发送服务器用于邮件的签名,另一个则公开在DNS中,供接收域尝试验证签名。生成这些密钥和安装它们的方法依赖于平台,超出了本文的范围,虽然稍后我将描述在DNS中发布公用DKIM密钥。
DKIM-Signature 标头
为了开始了解DKIM,我们先看一个DKIM-Signature头:
DKIM-Signature: v=1; a=rsa-sha256; d=welcome.foo.com; s=notices; c=relaxed/relaxed; q=dns/txt; i=@welcome.foo.com; t=1454417737; h=From:Reply-To:Subject:Date:Message-ID:To:MIME-Version:Content-Type; bh=e+6RkdhJe69wcQKtRKw9rpDgkkPPbZ8Xwj/2Hi243Sc=; b=KhK4OjejS4QEBr1RwL/naZKBNLoFnR/3lmDOWZC3av4c2aH5Yg/D4vqhh1CpcyfP vRm7cp5EvrnPEsOA7r3E15jarzNFNHXtwjxCFn4g8StsXFOio9vHkO7bmp6t2aLu 8bPkX6cNHgULYS6TdqYd65y5xCDMEaQ9a3mnhF2TQss=;
DKIM-Signature头是一系列的键值对,其中一些可能比其他对读者更有兴趣,但我会在这里描述所有的。
首先,我们来看看那些读者大致感兴趣的部分:
v=1; – 指定DKIM版本(1是唯一的有效值)
a=rsa-sha256; – 用于构建加密哈希的算法
c=relaxed/relaxed; – 有两套关于在头和体中去除空白的规则,可以在创建DKIM签名的哈希时应用;这些规则称为“规则化规则”(因此键为c),而规则集要么是“relaxed”要么是“strict”。
t=1454417737; – 签名创建时的时间戳。
这三个头部分包含实际签名信息:
bh=e+6RkdhJe69wcQKtRKw9rpDgkkPPbZ8Xwj/2Hi243Sc=; – 这是邮件正文的哈希。
h=From:Reply-To:Subject:Date:Message-ID:To:MIME-Version:Content-Type; – 这是一列用于创建下面显示的签名数据的头。
b=KhK4OjejS4QEBr1RwL/naZKBNLoFnR/3lmDOWZC3av4c2aH5Yg/D4vqhh1CpcyfPvRm7cp5EvrnPEsOA7r3E15jarzNFNHXtwjxCFn4g8StsXFOio9vHkO7bmp6t2aLu8bPkX6cNHgULYS6TdqYd65y5xCDMEaQ9a3mnhF2TQss=; – 这是实际的DKIM签名数据
这三个部分是接收服务器最关注以验证签名的重要内容:
d=welcome.foo.com; – 这标识了签署消息的域
s=notices; – 选择器;域可以在签署消息时使用多个选择器。
i=@welcome.foo.com; – 这是代表消息签署的身份。语法上,这看起来像电子邮件地址,甚至可以是一个;电子邮件地址的本地部分可以是空的,如本例中所示,而域部分必须与签名中的d=部分的域相同或是其子域。
这些部分受接收服务器关注的原因是,它们提供必要的信息以帮助接收方验证签名。
为了开始了解DKIM,我们先看一个DKIM-Signature头:
DKIM-Signature: v=1; a=rsa-sha256; d=welcome.foo.com; s=notices; c=relaxed/relaxed; q=dns/txt; i=@welcome.foo.com; t=1454417737; h=From:Reply-To:Subject:Date:Message-ID:To:MIME-Version:Content-Type; bh=e+6RkdhJe69wcQKtRKw9rpDgkkPPbZ8Xwj/2Hi243Sc=; b=KhK4OjejS4QEBr1RwL/naZKBNLoFnR/3lmDOWZC3av4c2aH5Yg/D4vqhh1CpcyfP vRm7cp5EvrnPEsOA7r3E15jarzNFNHXtwjxCFn4g8StsXFOio9vHkO7bmp6t2aLu 8bPkX6cNHgULYS6TdqYd65y5xCDMEaQ9a3mnhF2TQss=;
DKIM-Signature头是一系列的键值对,其中一些可能比其他对读者更有兴趣,但我会在这里描述所有的。
首先,我们来看看那些读者大致感兴趣的部分:
v=1; – 指定DKIM版本(1是唯一的有效值)
a=rsa-sha256; – 用于构建加密哈希的算法
c=relaxed/relaxed; – 有两套关于在头和体中去除空白的规则,可以在创建DKIM签名的哈希时应用;这些规则称为“规则化规则”(因此键为c),而规则集要么是“relaxed”要么是“strict”。
t=1454417737; – 签名创建时的时间戳。
这三个头部分包含实际签名信息:
bh=e+6RkdhJe69wcQKtRKw9rpDgkkPPbZ8Xwj/2Hi243Sc=; – 这是邮件正文的哈希。
h=From:Reply-To:Subject:Date:Message-ID:To:MIME-Version:Content-Type; – 这是一列用于创建下面显示的签名数据的头。
b=KhK4OjejS4QEBr1RwL/naZKBNLoFnR/3lmDOWZC3av4c2aH5Yg/D4vqhh1CpcyfPvRm7cp5EvrnPEsOA7r3E15jarzNFNHXtwjxCFn4g8StsXFOio9vHkO7bmp6t2aLu8bPkX6cNHgULYS6TdqYd65y5xCDMEaQ9a3mnhF2TQss=; – 这是实际的DKIM签名数据
这三个部分是接收服务器最关注以验证签名的重要内容:
d=welcome.foo.com; – 这标识了签署消息的域
s=notices; – 选择器;域可以在签署消息时使用多个选择器。
i=@welcome.foo.com; – 这是代表消息签署的身份。语法上,这看起来像电子邮件地址,甚至可以是一个;电子邮件地址的本地部分可以是空的,如本例中所示,而域部分必须与签名中的d=部分的域相同或是其子域。
这些部分受接收服务器关注的原因是,它们提供必要的信息以帮助接收方验证签名。
为了开始了解DKIM,我们先看一个DKIM-Signature头:
DKIM-Signature: v=1; a=rsa-sha256; d=welcome.foo.com; s=notices; c=relaxed/relaxed; q=dns/txt; i=@welcome.foo.com; t=1454417737; h=From:Reply-To:Subject:Date:Message-ID:To:MIME-Version:Content-Type; bh=e+6RkdhJe69wcQKtRKw9rpDgkkPPbZ8Xwj/2Hi243Sc=; b=KhK4OjejS4QEBr1RwL/naZKBNLoFnR/3lmDOWZC3av4c2aH5Yg/D4vqhh1CpcyfP vRm7cp5EvrnPEsOA7r3E15jarzNFNHXtwjxCFn4g8StsXFOio9vHkO7bmp6t2aLu 8bPkX6cNHgULYS6TdqYd65y5xCDMEaQ9a3mnhF2TQss=;
DKIM-Signature头是一系列的键值对,其中一些可能比其他对读者更有兴趣,但我会在这里描述所有的。
首先,我们来看看那些读者大致感兴趣的部分:
v=1; – 指定DKIM版本(1是唯一的有效值)
a=rsa-sha256; – 用于构建加密哈希的算法
c=relaxed/relaxed; – 有两套关于在头和体中去除空白的规则,可以在创建DKIM签名的哈希时应用;这些规则称为“规则化规则”(因此键为c),而规则集要么是“relaxed”要么是“strict”。
t=1454417737; – 签名创建时的时间戳。
这三个头部分包含实际签名信息:
bh=e+6RkdhJe69wcQKtRKw9rpDgkkPPbZ8Xwj/2Hi243Sc=; – 这是邮件正文的哈希。
h=From:Reply-To:Subject:Date:Message-ID:To:MIME-Version:Content-Type; – 这是一列用于创建下面显示的签名数据的头。
b=KhK4OjejS4QEBr1RwL/naZKBNLoFnR/3lmDOWZC3av4c2aH5Yg/D4vqhh1CpcyfPvRm7cp5EvrnPEsOA7r3E15jarzNFNHXtwjxCFn4g8StsXFOio9vHkO7bmp6t2aLu8bPkX6cNHgULYS6TdqYd65y5xCDMEaQ9a3mnhF2TQss=; – 这是实际的DKIM签名数据
这三个部分是接收服务器最关注以验证签名的重要内容:
d=welcome.foo.com; – 这标识了签署消息的域
s=notices; – 选择器;域可以在签署消息时使用多个选择器。
i=@welcome.foo.com; – 这是代表消息签署的身份。语法上,这看起来像电子邮件地址,甚至可以是一个;电子邮件地址的本地部分可以是空的,如本例中所示,而域部分必须与签名中的d=部分的域相同或是其子域。
这些部分受接收服务器关注的原因是,它们提供必要的信息以帮助接收方验证签名。
DKIM Validation
除了提到的要求,即i=域必须与d=域相同或是d=域的子域,验证器使用d=和s=部分在DNS中查找签署者的公开DKIM密钥。该密钥是DNS中的TXT记录,总是位于selector._domainkey.domain位置。因此,在我们的例子中,使用s=notices和d=welcome.foo.com,公开DKIM密钥将在DNS中找到notices._domainkey.welcome.foo.com,它可能看起来像这样:
notices._domainkey.welcome.foo.com. descriptive text "v=DKIM1\; h=sha256\; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlXNDEHOstbxTkS0tjqy9qw2J 1mnjW5FBWQ4dyrYfrkr8/9VrtAY+eWcKMLUcR3mGFpk9QeHCXoILMJ22TmP1JfhzN NoCcMLffy39eWZKmtm4/Ry29qWBFvn2LKl5W3BBC3e4wQ14l+CQqY4C0QifIrPBwR pod8n+//qIpQIDAQAB\; s=email"
验证器使用该密钥(p=部分)生成信息的一组哈希值;如果这些哈希值匹配,则表示信息在传输过程中未被更改,因此,信息可以为签署者的声誉作出贡献,甚至可能从中受益。
除了提到的要求,即i=域必须与d=域相同或是d=域的子域,验证器使用d=和s=部分在DNS中查找签署者的公开DKIM密钥。该密钥是DNS中的TXT记录,总是位于selector._domainkey.domain位置。因此,在我们的例子中,使用s=notices和d=welcome.foo.com,公开DKIM密钥将在DNS中找到notices._domainkey.welcome.foo.com,它可能看起来像这样:
notices._domainkey.welcome.foo.com. descriptive text "v=DKIM1\; h=sha256\; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlXNDEHOstbxTkS0tjqy9qw2J 1mnjW5FBWQ4dyrYfrkr8/9VrtAY+eWcKMLUcR3mGFpk9QeHCXoILMJ22TmP1JfhzN NoCcMLffy39eWZKmtm4/Ry29qWBFvn2LKl5W3BBC3e4wQ14l+CQqY4C0QifIrPBwR pod8n+//qIpQIDAQAB\; s=email"
验证器使用该密钥(p=部分)生成信息的一组哈希值;如果这些哈希值匹配,则表示信息在传输过程中未被更改,因此,信息可以为签署者的声誉作出贡献,甚至可能从中受益。
除了提到的要求,即i=域必须与d=域相同或是d=域的子域,验证器使用d=和s=部分在DNS中查找签署者的公开DKIM密钥。该密钥是DNS中的TXT记录,总是位于selector._domainkey.domain位置。因此,在我们的例子中,使用s=notices和d=welcome.foo.com,公开DKIM密钥将在DNS中找到notices._domainkey.welcome.foo.com,它可能看起来像这样:
notices._domainkey.welcome.foo.com. descriptive text "v=DKIM1\; h=sha256\; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDlXNDEHOstbxTkS0tjqy9qw2J 1mnjW5FBWQ4dyrYfrkr8/9VrtAY+eWcKMLUcR3mGFpk9QeHCXoILMJ22TmP1JfhzN NoCcMLffy39eWZKmtm4/Ry29qWBFvn2LKl5W3BBC3e4wQ14l+CQqY4C0QifIrPBwR pod8n+//qIpQIDAQAB\; s=email"
验证器使用该密钥(p=部分)生成信息的一组哈希值;如果这些哈希值匹配,则表示信息在传输过程中未被更改,因此,信息可以为签署者的声誉作出贡献,甚至可能从中受益。
验证失败和故障排除
我在上面提到,DKIM 失败可能很难排查,我将在这里解释原因。
一些 DKIM 验证失败有明显的原因,例如消息没有被签名,或签名域的公钥在 DNS 中找不到或语法错误,或者消息在传输中显然被修改了。当这些类型的失败发生时,很容易找出问题并推荐解决方案。然而,困难的地方,以及导致最令人沮丧的支持经历的地方,是那些消息被签名了,公钥存在于 DNS 中,消息没有显然被更改,但验证程序报告签名验证失败的情况。
这些问题很难排查的原因是,因为无法真正让任何一方再现消息签名和验证时的情况。消息在其 DKIM-Signature 头中有签名时生成的哈希值,但验证者可能无法访问签名者的基础设施,因此无法在签名者的条件下重现签名。类似地,签名者可能无法访问验证者的基础设施,因此无法尝试以验证者进行验证消息的方式进行验证。
我描述的此类失败是罕见的事件,而 DKIM 验证失败本身通常不会对投递位置产生影响。虽然 DKIM 处理消息验证,实施全面的电子邮件验证技术 确保您发送给能够实际接收和验证您消息的合法地址。据我的经验,这种失败会比其他任何类型的 DKIM 问题产生更多的支持票。
我在上面提到,DKIM 失败可能很难排查,我将在这里解释原因。
一些 DKIM 验证失败有明显的原因,例如消息没有被签名,或签名域的公钥在 DNS 中找不到或语法错误,或者消息在传输中显然被修改了。当这些类型的失败发生时,很容易找出问题并推荐解决方案。然而,困难的地方,以及导致最令人沮丧的支持经历的地方,是那些消息被签名了,公钥存在于 DNS 中,消息没有显然被更改,但验证程序报告签名验证失败的情况。
这些问题很难排查的原因是,因为无法真正让任何一方再现消息签名和验证时的情况。消息在其 DKIM-Signature 头中有签名时生成的哈希值,但验证者可能无法访问签名者的基础设施,因此无法在签名者的条件下重现签名。类似地,签名者可能无法访问验证者的基础设施,因此无法尝试以验证者进行验证消息的方式进行验证。
我描述的此类失败是罕见的事件,而 DKIM 验证失败本身通常不会对投递位置产生影响。虽然 DKIM 处理消息验证,实施全面的电子邮件验证技术 确保您发送给能够实际接收和验证您消息的合法地址。据我的经验,这种失败会比其他任何类型的 DKIM 问题产生更多的支持票。
我在上面提到,DKIM 失败可能很难排查,我将在这里解释原因。
一些 DKIM 验证失败有明显的原因,例如消息没有被签名,或签名域的公钥在 DNS 中找不到或语法错误,或者消息在传输中显然被修改了。当这些类型的失败发生时,很容易找出问题并推荐解决方案。然而,困难的地方,以及导致最令人沮丧的支持经历的地方,是那些消息被签名了,公钥存在于 DNS 中,消息没有显然被更改,但验证程序报告签名验证失败的情况。
这些问题很难排查的原因是,因为无法真正让任何一方再现消息签名和验证时的情况。消息在其 DKIM-Signature 头中有签名时生成的哈希值,但验证者可能无法访问签名者的基础设施,因此无法在签名者的条件下重现签名。类似地,签名者可能无法访问验证者的基础设施,因此无法尝试以验证者进行验证消息的方式进行验证。
我描述的此类失败是罕见的事件,而 DKIM 验证失败本身通常不会对投递位置产生影响。虽然 DKIM 处理消息验证,实施全面的电子邮件验证技术 确保您发送给能够实际接收和验证您消息的合法地址。据我的经验,这种失败会比其他任何类型的 DKIM 问题产生更多的支持票。



