V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
sNullp
V2EX  ›  分享发现

了解现代 SMTP 和电邮 Anti Spam 协议(SPF, DKIM, DMARC)

  •  1
     
  •   sNullp · 85 天前 · 1637 次点击
    这是一个创建于 85 天前的主题,其中的信息可能已经有所发展或是发生改变。

    SMTP 协议

    具体的协议本身就不多介绍了,各位可以参考维基百科: https://en.wikipedia.org/wiki/Simple_Mail_Transfer_Protocol

    这里只指出一个重点:SMTP 将电邮区分为 Envelope 和 Data 两部分。通常我们在邮件客户端里见到的所有东西都属于 DATA ,包括 From:和 To:字段(也就是我们通常见到的收件人发件人)。然而所谓 Envelope 也有 MAIL FROM 和 RECP TO 数据,并且这二者和 DATA 里的 FROM/TO 一般并不一致!下文将详细展开。

    Agents

    现代 SMTP 把一封电邮送达的过程中的处理程序分为很多 Agent:

    • MUA: Mail user agent ,也就是所谓的邮件客户端譬如 Thunderbird 。在用 Webmail 的情况下其实你看到的网页就是 MUA 。
    • MSA: Mail submission agent ,是邮件服务提供商向用户提供的第一站,也就是邮件客户端里常说的“SMTP 服务器”。
    • MTA: Mail transfer agent ,可以中转( relay )邮件。
    • MDA: Mail deliver agent ,是邮件目标信箱的服务器。用户 MUA 可以联系它来取走邮件( POP3 协议)。

    一个标准送达流程是:

    MUA -> MSA -> MTA -> ... -> MTA -> MDA -> MUA

    除了最后 MDA 到 MUA 以外,其实所有 agent 之间都通过 SMTP 协议联系。因此协议的行为其实因为 agent 的不同而有产生了区别:

    MUA -> MSA:

    可以理解为邮件进入互联网的第一站。MSA 负责验证邮件的真实性( Authentication ),也就是验证对应的 MUA 确实有权利发送这封邮件。通常这里会用到 SMTP Auth 来实现,也就是邮件服务商向用户要求一组用户名和密码。以及,MSA 会对电子邮件进行 DKIM 签名。

    MSA -> MTA, MTA -> MTA, MTA -> MDA:

    在邮件 relay 的过程中,这三者其实是等价的(也就是说存在 MSA 直接联系到 MDA 的情况。如果邮件再同一个服务商内中转,譬如 gmail 发送给 gmail ,那甚至此时 MSA 和 MDA 可以是同一个程序)。接受邮件的 Agent 也会检查送来邮件 Agent 的真实性,但并不是通过用户名和密码(下文会详细描述)。

    值得注意的是,在所有的这些 Agent 中,程序是通过 Envelope 的 MAIL FROM 和 RECP TO 来决定如何邮件的来龙去脉的。DATA 里的 FROM 和 TO 并不决定邮件的去向。我们下面分开讨论。

    MAIL FROM

    通常来说,除了 MUA 以外,所有的 Agent 都会将 Mail From 设置成一个自己控制的值。举一个具体的例子,我拥有的 c7.io 域名配置了 CF 的 mail forward ,也就是发送给任何 [email protected] 的电邮都会被转发到我的 gmail 邮箱。

    那么,如果一个 outlook.com 信箱向 [email protected] 发邮件,信件会如此传递:

    • outlook MSA 向 c7.io MTA (也就是 CF 的 MTA )送邮件,Mail From 是 [email protected] (注意哦这里并不是发件人,而是 outlook 自己生成的一个 identifier )

    • c7.io MTA 向 gmail MDA 送邮件,Mail From 是 [email protected] (此处就变成 c7.io 了)

    可见,MAIL FROM 每次都会被改变,而且其中 identification 仍然能 uniquely identify 对应的这封电子邮件。这么做的目的是允许邮件服务商对 bounce-back 的邮件做额外的处理,并对接下来 Anti spam 的协议提供了机会。

    RECP TO

    既然说到了 MAIL FROM 就提一下 RECP TO 。这里和 spam 没什么关系,RECP TO 一般来说和 DATA 里的 TO+CC 一致,除非有 bcc 的情况。如果是 bcc 那么目标邮件地址不会存在于 DATA 中(所以才叫 blind ),只会出现在 RECP TO 里。

    Anti spam

    SMTP 协议本身设计的时候并没怎么考虑到 SPAM 的问题。所以协议本身有很多补丁。上面已经说过 MUA 到 MSA 时 MSA 会检查用户的用户名和密码,并且会很显然地检查 DATA 里的 FROM 字段确保这个登录进来的用户有权利以这个 FROM 的身份发邮件。然而在 MSA ,MTA 和 MDA 交流的过程中,大家都是不同服务商等价的程序,因此不存在谁事先在另外一方设置密码的可能性。所以,SPF 和 DKIM 就出现了。

    SPF

    https://en.wikipedia.org/wiki/Sender_Policy_Framework

    当邮件在 MSA ,MTA 和 MDA 之间传输时,SPF 是一个很简单明了的协议。接收方会去 query 发送方域名的 SPF 字段。这个字段会指明哪些 IP 是这个域名允许的 sender 。譬如,当 c7.io MTA 向 gmail MDA 发邮件时,gmail 服务器会 query 到 c7.io 拥有 SPF record:v=spf1 include:_spf.mx.cloudflare.net ~all 。那么如果送邮件来的 IP 并不属于 mx.cloudflare.net ,那就 fail 了 SPF 。

    DKIM

    https://en.wikipedia.org/wiki/DomainKeys_Identified_Mail

    SPF 只关注每个 agent 前一跳的身份,却并不能阻止一个恶意的 MTA 伪造邮件。所以出现了 DKIM 。当一封邮件进入到每一个 Agent 时( MUA 除外),这个 Agent 都会对邮件的数个 Header 和 Body (都处于 DATA 字段)进行签名并将签名内容也放在 DATA 字段里。Agent 签名会使用一个私钥,而其对应的公钥会在对应的域名的 dkim 记录里公布。从 DKIM 的精神来看,这个协议的目的是为了验证 DATA 中描述的邮件没有在传输中被篡改,也就是说,MSA 生成的 DKIM 签名是最有价值的。但实践中,似乎每一个 agent 插入到签名都会被接收者验证。

    举个具体的例子:还是 outlook->c7.io->gmail ,outlook MSA 会插入一个用 outlook 私钥签字的签名。当 c7.io MTA 收到后,它会去 query outlook.com 的 DKIM record ,也就是 outlook 的公钥,并且用它来验证签名是否有效。然后 c7.io MTA 也会将现有的 DATA 再一次用 c7.io 的私钥签名。当 gmail MDA 收到邮件后,它会 query both outlook and c7.io 的公钥(譬如 k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDckJFiBtn29uLex8LM2DG4zvZ9doM9v8veISK5rAoS2yU517rqZN/gYGwhKVuvfmp86OJGKG2Z6SQG9JmcNQ7rGiVE6X99M71hm449ShkF29hG65lI9sFpjf/67bjnQcgwwj6q4aNKb9Rh3zc/gV4jtz+vfzaMTTcAdZbd8hKX3wIDAQAB ),然后验证两个签名是否都有效。一旦发现签名错误,就是 DKIM fail 。

    DMARC

    https://en.wikipedia.org/wiki/DMARC

    DMARC 的目的是告诉 Agent ,如果 SPF 或者 DKIM fail 了,该如何处理这封邮件。DMARC 也存在于域名 DNS 记录中。DMARC 可以要求 agent 继续放行电邮(可能不会被遵守),但更通常是要求 agent 把邮件设为 spam ,并通知某个指定的地址。

    其它

    很多 Agent 会增加别的验证。譬如 Gmail 要求所有来信的 IP 的 reverse DNS 和其 MAIL FROM 的域名必须对应。这似乎和 SPF 正好是镜像。

    用自己的域名收发邮件

    总结上面的内容,如果想用自己的域名收发邮件,需要做到:

    • 发邮件

    需要正确配置 SPF ,DKIM ,DMARC 不是必须的但是推荐。并且要和一个拥有对应 DKIM 私钥的 MSA 配合使用(这也是为什么现代不通过一个 mail relay (譬如 AWS SES ,Mailgun)直接发送一封邮件如此之难)。

    • 收邮件

    仅需要配置域名的 MX ,指向 MDA 。一般来说大家都是用第三方的服务,所以 MX 指向第三方的服务器就行。

    总结

    SMTP 是一个典型的设计时没有考虑周全从而不断打补丁的例子。目前至少 Gmail 和 Outlook 都提供了"View Original"的功能,可以详细看到邮件原始的 DATA 部分。里面都会详细描述每一个 MTA 所插入的内容,包括 Agent 得到的 Envelope 的地址,其所做的 SPF ,DKIM 检查,和新的 DKIM 签名。大家可以去自己的邮箱里看看,和文章的内容对照一下。

    本文在某些细节上讲的比较粗略,因为某些复杂的地方很难用白话语言说清楚。我希望这篇文章能起到一个提纲的作用。具体生产中如果遇到了问题还是要去读协议标准。

    9 条回复    2024-01-31 10:24:34 +08:00
    sNullp
        1
    sNullp  
    OP
       85 天前
    自己消灭 0 回复😂 ……
    qiguai2017
        2
    qiguai2017  
       85 天前
    写得很好,可惜国内的云服务器很难申请到 25 端口。
    gdfsjunjun
        3
    gdfsjunjun  
       85 天前
    写得非常不错。
    我发现网易邮箱检测很严,如果你域名没配置 SPF 、DKIM 之类的,是会被拒收或者送达垃圾箱里的。
    mason961125
        4
    mason961125  
       85 天前
    @qiguai2017 如果有批量发邮件需求,还是直接用 SasS 吧,你自己即使有 25 开放的机器,设置好了 SPF/DKIM + rdns 很大概率因为是陌生 IP 也是直接进垃圾箱。
    deorth
        5
    deorth  
       85 天前 via Android
    写得很好,可惜这些我基本都已经被迫学过了
    datocp
        6
    datocp  
       85 天前 via Android
    总有方法的,从玩电脑起就在寻找免费 smtp
    直到 stunnel gmail 方法的实现,这也是目前在 vps 直接调用 qqmail 的实现,stunnel 真棒!另外一个叫 emailrelay 。
    julyclyde
        7
    julyclyde  
       84 天前
    在中国,这个业务已经可以算消失了
    Ayanokouji
        8
    Ayanokouji  
       76 天前
    请问,MTA -> MTA ,是如何确定地址的,是查询 mx 记录吗,使用的都是统一的 25 端口吗
    sNullp
        9
    sNullp  
    OP
       76 天前   ❤️ 1
    @Ayanokouji 是的,是的
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   995 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 69ms · UTC 20:15 · PVG 04:15 · LAX 13:15 · JFK 16:15
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.