您如何验证电子邮件是从正确的域发送的,并且电子邮件的内容没有被修改?

How can you verify that an email was sent from the proper domain and that the contents of the email haven't been modified?

我正在检查我的电子邮件 headers(从 Gmail 发送),我想知道哪些最适合验证电子邮件是从正确的域发送的,并且电子邮件的内容没有被泄露修改了吗?

我将使用从 Stack Overflow 收到的示例消息来说明您可以执行哪些操作来手动验证消息。

SPF 在 after 投递后用处不大,因为它是验证邮件来源的相对次要(但重要)的步骤,但很高兴知道它当时很好,header 告诉你的。与传递相关的消息内容部分是接收者添加的 Return-path header,以及显示接收来源的最后 Received header:

Return-Path: <bounces+3553988-07ba-marcus=example.com@em.Whosebug.email>
Received: from o1.em.Whosebug.email (o1.em.Whosebug.email [167.89.81.234])
    (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits))
    (No client certificate requested)

在 DNS 中查找此 em.Whosebug.email 域作为 TXT 记录给我们:

# dig txt +short em.Whosebug.email
u3553988.wl239.sendgrid.net.
"v=spf1 ip4:167.89.81.234 ip4:167.89.85.72 ip4:168.245.32.199 -all"

并且我们可以看到 Received header 中出现的 167.89.81.234 IP 被明确地列在了记录中,所以 SPF check out。其他域可能有更复杂的需求来验证 SPF,例如include 机制需要额外的 DNS 查找。值得注意的是,SPF 甚至不查看 From header 中使用的地址;它可以与 return 路径域完全无关。

DKIM 才是真正的行动所在。您可以使用 DKIM-signature header 中的信息来验证消息是否未被篡改。例如:

DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; 
    d=Whosebug.email; 
    h=from:subject:to:reply-to:mime-version:content-type; s=s1; 
    bh=IgKJJZNcUhfjH6LDr4XaWr3pBwq6wwxwyrGmf+k3DVo=; b=rR4J7VyvF3i0N
    20IX9bx0LGTKpSKj7XoHJurhBjcZLLTn/hXuZ8OMehfgMNFeXaMljlOz4tfoFwit
    aJ8UtK1oMVCPiv9200hpQViCh/5VsyYbs6k3YN6R3cFxMbrb7nflodXX+4Rp4xBu
    T+CloNFEDICtWJT4bVSrs/NRAUlJWY=

查看 From header 并检查它是否与 DKIM 签名的 d 字段中显示的域匹配,它确实如此:

From: Stack Exchange <do-not-reply@Whosebug.email>

DKIM 签名有 2 个签名部分:b 是消息 body 的签名,bh 是消息 header 的签名。这些的交互是相当棘手的。要生成签名,首先计算 body 签名,然后计算 header 签名 其中包括 body 签名 ,以及h 元素中列出的规范化消息 header 除了 bh 元素的实际值,因为那会呈现先有鸡还是先有蛋问题。 bh 签名仅包含列出的 header,因为邮件服务器可能会添加其他 不应 或未知的 header在发送时,例如 Return-pathReceived。 Stack overflow 的签名不寻常,因为它不包含 Date header,因此可以在不破坏 DKIM 签名的情况下更改消息上的日期。

要验证签名,您需要签名时使用的 public 密钥,您可以使用 s 字段从 DNS 获取该密钥,该字段提供选择器,在本例中为 s1:

# dig txt +short s1._domainkey.Whosebug.email
s1.domainkey.u3553988.wl239.sendgrid.net.
"k=rsa; t=s; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCyJRzkL/aRo1F1+ChY+Crt2TARqqo7tGATw3fMfzW8MXFWaoW1rSvZsq4k1EIf2iW7gO/QZjU1Td7h1aZpS63/CmpKymmqNbHnnbTxZGvZziKPcL/R2PVL0g88MFcpAuSjIsGysYTeow0mnXQ5W03z5mtWqm5nxNM40A/TIlOegwIDAQAB"

此签名的一个问题是它仅使用 1024 位密钥; gmail 会忽略少于 2048 位的 DKIM 签名,所以如果 SO 对 gmail 有可传递性问题,我不会感到惊讶。

实际上验证 DKIM 签名是一个复杂的过程,特别是如果它使用 relaxed 规范化算法(这使得签名更有可能在电子邮件旅程中存活下来)。我写了 a PHP DKIM validator,你可以用它来验证你自己的消息。

最后一层是 DMARC,它将 SPF、DKIM 和 From 地址 header 联系在一起。回顾Return-path,我们可以看到return路径域是Fromheader中使用的域的子域。这意味着它符合 "relaxed" 而不是 "strict" 对齐。我们可以看到SO的DMARC记录:

# dig txt +short _dmarc.Whosebug.email
"v=DMARC1;p=reject;sp=reject;pct=100;rua=mailto:dmarc-aggregates@whosebug.com;ruf=mailto:dmarc-forensics@whosebug.com;fo=1"

这告诉我们,SO 希望收件人严格执行 SPF 和 DKIM 检查,并拒绝任何未签出的邮件,并且他们已设置地址以接收任何失败邮件的摘要和取证报告。

还有很多细节,但这不是真正适合它的地方。有一篇关于这一切的好文章 here