为什么Offer邮件会丢失(简述电子邮件)

今天我们来谈谈一个熟悉却又陌生的东西——电子邮件,我们每天都在接收或者发送电子邮件,有些邮件可以到达我们的收件箱,而有些邮件会出现在我们邮箱的Junk/Spam目录,甚至会被邮件服务器直接拒收。作者这两天参加春招就遇到这么一件事情,被告知Offer邮件已经发送到我的邮箱,等了许久却没有收到,检查邮件服务器的日志发现邮件状态是DEFER,被邮件服务器延迟接收。为什么会这样?电子邮件是怎么工作的?让我们来简单看看。(本文简单聊聊电子邮件,如有错误,还请批评指正。🥰)

SMTP

像许多网络协议一样,电子邮件相关协议的岁数比我还大。或许反直觉的是,电子邮件本身是不加密的,电子邮件就是一堆纯文本在复杂的网络上传来传去。发送一封邮件到对方服务器常用的是SMTP协议,这个协议一开始便基于ASCII纯文本,后来才使用MIME标准让图像、声音等二进制可以在电子邮件中传输。对于这么一个协议,如果对方的SMTP服务器没有加密,我们可以使用nc直接发一封邮件:

Send email with nc

这里,我们向www.mail-tester.com发送了一封测试邮件,带有状态码的行是对方给我们的回复,除此之外都是我们发送的数据。发送一封电子邮件是不是比我们想得简单?是的,这样对方就收到了我们的邮件。(如果对方使用了SSL/TLS,可以考虑使用swaks或者openssl,不过大同小异)但是,这封邮件的命运如何呢?

Your email may never see the light of an inbox

毫不意外,我们的邮件永远不会出现在对方的收件箱中,为什么会这样?Mail tester给出了以下理由:

  • MISSING_DATE Missing Date: header
  • MISSING_FROM Missing From: header
  • MISSING_HEADERS Missing To: header
  • MISSING_MID Missing Message-Id: header
  • RDNS_NONE Delivered to internal network by a host with no rDNS
  • SPF_FAIL SPF: sender does not match SPF record (fail)
  • SPF_HELO_FAIL SPF: HELO does not match SPF record (fail)

让我们加一点Header:

1
2
3
4
5
6
7
8
9
Date: Fri, 07 Apr 2023 01:10:32 +0000 (UTC)
From: Test <sender@example.com>
To: Test <test-enjevs04y@srv1.mail-tester.com>
Message-ID: <5161dbc0-16d0-7a08-20ad-ba315ac2d1f0@example.com>
Subject: Test Email

Hello, world!

.

评分从0.1上升到了3.8,看上去还行!让我们继续。

Headers

相比较第一封邮件,我们添加了一些邮件Header,让我们问问GPT-4有哪些常见的Header:

  • From:发件人的电子邮件地址。这个字段是必需的,表示邮件的发送者。
  • To:收件人的电子邮件地址。这个字段也是必需的,表示邮件的接收者。
  • Cc(抄送):向其他收件人发送邮件副本的电子邮件地址。这个字段是可选的。
  • Bcc(密件抄送):向其他收件人发送邮件副本的电子邮件地址,但其他收件人看不到这些 Bcc 地址。这个字段是可选的。
  • Subject:邮件的主题。这个字段通常包含邮件的简短摘要,以帮助收件人理解邮件的内容。
  • Date:邮件发送的日期和时间。这个字段通常由邮件客户端自动填充。
  • Message-ID:邮件的唯一标识符。这个字段通常由邮件客户端自动生成,用于跟踪和引用特定的邮件。
  • Content-Type:邮件正文的 MIME 类型,例如 text/plain(纯文本)或 text/html(HTML 格式)。
  • Content-Transfer-Encoding:邮件正文的编码方式,例如 7bit、8bit、binary、quoted-printable 或 base64。
  • MIME-Version:邮件使用的 MIME(多用途因特网邮件扩展)协议版本,通常为 1.0。
  • Reply-To:指定回复邮件时应发送到的电子邮件地址。这个字段是可选的,如果没有指定,回复通常会发送到发件人地址。
  • In-Reply-To:引用或回复的原始邮件的 Message-ID。这个字段用于将相关邮件组织成线程。
  • References:一个包含其他相关邮件 Message-ID 的列表,用于将邮件组织成线程。
  • X-*:自定义 Header 字段,用于添加特定于应用程序的信息。这些字段通常以 X- 开头,以区分它们与标准 Header 字段。

对于其中的Message-ID和In-Reply-To,是判断邮件Thread的重要依据,如果In-Reply-To的ID和之前Message-ID相同,这会帮助邮件客户端正确处理邮件回复。

SPF

就算我们添加了Header,邮件分数还是不是很高,这时候我们来看看SPF。SPF是一种特殊的DNS记录,可用于确保邮件由授权的服务器发出,否则谁都可以连接到邮件服务器,声称自己是example.com或者是其他域名。让我们找到一个不怎么用的域名,给它加上SPF记录,然后再来看看。假如我们的域名是example.com,我们想允许IP是1.1.1.1的服务器能够代表我们发信,那么我们给@加一条TXT:v=spf1 ip4:1.1.1.1 -all,具体的规则可以看看参考资料的百科页面。设置好后,我们再来发信:

SPF passed

不错不错!除了SPF,还有一些安全措施。

DKIM

DKIM也是为了验证电子邮件是否真正来自发件人生成的域的手段,我们来看看GPT-4的解释其工作原理:

  1. 设置DKIM记录:首先,发件人域的管理员需要生成一对公钥和私钥。公钥将作为一个DNS TXT记录发布在域的DNS设置中,而私钥则需要妥善保管,以便发件服务器使用。
  2. 签名邮件:当发件服务器准备发送电子邮件时,它会使用私钥对邮件的某些部分(通常是邮件头和正文的一部分)进行数字签名。签名会包含在一个名为DKIM-Signature的邮件头字段中。
  3. 接收服务器验证:收件服务器在收到带有DKIM-Signature的邮件时,首先从该签名中获取发件人域的相关信息。然后,收件服务器会查询发件人域的DNS记录,获取相应的公钥。
  4. 验证签名:收件服务器使用获取到的公钥对DKIM-Signature中的签名进行验证。如果验证成功,说明邮件确实来自声称的发件人域,并且在传输过程中没有被篡改。否则,邮件可能被视为伪造或者被篡改。

DMARC

DMARC建立在SPF和DKIM之上,设置了如果未通过以上两种方法验证该如何处理电子邮件,可以查看DMARC Overview。一般我们设置为v=DMARC1; p=quarantine;

Blacklist

评价就是不要用黑名单,为什么?我想在这里引用部分来自Messages might go to Junk - Migadu的话,Migadu是我比较喜欢的邮件服务器提供商:使用黑名单不是发件人的问题,而是收件人的问题,黑名单只是一个判断垃圾邮件的指标,更有部分黑名单需要收取删除费用,让他们成为勒索者。正常邮件就是正常邮件,和它是否从来自黑名单IP中发出没有关系。

Greylist

终于,我们可以来讲讲之前的Offer邮件为什么没有收到了。收到的Offer邮件经过邮件服务器的检查后,会给出一个“分数”,我们可以在邮件的Header中看到X-Spam-Score,如果负数越小说明更不像是垃圾邮件,正数越大越像是垃圾邮件。如果垃圾邮件分数介于正常邮件和垃圾邮件之间,邮件服务器会暂时拒绝这封邮件,告诉发信方重试发送。这时候有能力的送信服务器会将邮件保留在队列中重试发送,而大量垃圾邮件发送者不会这么做。如果重试发送了,邮件终将会送达收件箱。所以我怀疑是对方公司的邮件服务器没有做重试,导致邮件没有收到。以后看来得设置白名单看看能不能解决这个问题了。

参考资料