2024-04-21 20:21:07 +08:00

241 lines
15 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<title>Dich&#x27;blog</title>
<meta http-equiv="content-type" content="text/html; charset=utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5">
<meta name="robots" content="noodp"/>
<link rel="stylesheet" href="https://blog.dich.ink/style.css">
<link rel="stylesheet" href="https://blog.dich.ink/color/blue.css">
<link rel="stylesheet" href="https://blog.dich.ink/color/background_dark.css">
<link rel="stylesheet" href="https://blog.dich.ink/font-hack-subset.css">
<meta name="description" content="">
<meta property="og:description" content="">
<meta property="og:title" content="Dich'blog">
<meta property="og:type" content="article">
<meta property="og:url" content="https://blog.dich.ink/how-email-works-1/">
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:description" content="">
<meta name="twitter:title" content="Dich'blog">
<meta property="twitter:domain" content="blog.dich.ink">
<meta property="twitter:url" content="https://blog.dich.ink/how-email-works-1/">
<link rel="alternate" type="application/atom+xml" title="RSS" href="https://blog.dich.ink/atom.xml">
</head>
<body class="">
<div class="container">
<header class="header">
<div class="header__inner">
<div class="header__logo">
<a href="https://blog.dich.ink" style="text-decoration: none;">
<div class="logo">
Dich&#x27;blog
</div>
</a>
</div>
</div>
<nav class="menu">
<ul class="menu__inner">
<li class="active"><a href="https://blog.dich.ink">blog</a></li>
<li><a href="https://blog.dich.ink/tags">tags</a></li>
<li><a href="https://blog.dich.ink/archive">archive</a></li>
<li><a href="https://blog.dich.ink/about">about me</a></li>
<li><a href="https://blog.dich.ink/links">links</a></li>
<li><a href="https://github.com/Dichgrem" target="_blank" rel="noopener noreferrer">github</a></li>
</ul>
</nav>
</header>
<div class="content">
<div class="post">
<h1 class="post-title"><a href="https://blog.dich.ink/how-email-works-1/">电子邮件是如何工作的:SPF&#x2F;DKIM&#x2F;DMARC</a></h1>
<div class="post-meta-inline">
<span class="post-date">
2023-12-24
</span>
</div>
<span class="post-tags-inline">
:: tags:&nbsp;
<a class="post-tag" href="https://blog.dich.ink/tags/mail/">#Mail</a>&nbsp;
<a class="post-tag" href="https://blog.dich.ink/tags/tech/">#Tech</a></span>
<div class="post-content">
<p>前言 在互联网的日常使用中,电子邮件作为一项基础服务扮演着重要的角色。尽管在过去几十年里出现了各种新型的通讯方式,但电子邮件仍然保持着其不可替代的地位。了解电子邮件的工作原理,有助于更好地理解这一基础服务是如何运作的。</p>
<span id="continue-reading"></span><h2 id="yi-ji-yu-xie-yi-de-chuan-shu">一. 基于协议的传输</h2>
<p>与许多其他基于协议的应用一样,电子邮件依赖于一系列协议来进行传输和交换。而基于协议的应用一般不会轻易地被历史淘汰:在过去的几十年里,基于 HTTP 上层的网站,以及技术更新换代了好几波,但底层的协议依然还是 HTTPHTTPS。基于 BitTorrent 协议的文件交换协议和基于SMTPSimple Mail Transfer Protocol的电子邮件传输便是其中之一。</p>
<h2 id="er-you-jian-fa-song-de-liu-cheng">二. 邮件发送的流程</h2>
<p>电子邮件的发送过程可以简单地描述为以下几个步骤:</p>
<ul>
<li>用户在邮件客户端例如Gmail中撰写并发送一封邮件。</li>
<li>邮件客户端使用SMTP协议将邮件发送到相应的邮件服务器。</li>
<li>通过DNS查询MX记录找到接收方的邮件服务器。</li>
<li>发送邮件服务器使用SMTP协议将邮件传递给接收方的邮件服务器。</li>
<li>接收方的邮件服务器将邮件存储在相应的邮箱中,供用户查看。</li>
</ul>
<p>为了确保通信安全电子邮件的发送还引入了一些安全机制如SPF、DKIM和DMARC。</p>
<ul>
<li>SPFSender Policy Framework用于验证发件人的身份防止发件人伪造。</li>
<li>DKIMDomainKeys Identified Mail通过数字签名验证邮件的真实性。</li>
<li>DMARCDomain-based Message Authentication, Reporting, and Conformance结合了SPF和DKIM提供了更严格的邮件验证机制。</li>
</ul>
<h2 id="san-ju-ti-liu-cheng">三.具体流程</h2>
<p>假设用户 <a href="mailto:a@gmail.com">a@gmail.com</a> 发送一封邮件到 <a href="mailto:b@qq.com">b@qq.com</a>,会执行如下的流程。</p>
<p><strong>1.查询 MX 记录</strong></p>
<p>当我们在 Gmail 网页上撰写一封邮件并点击发送按钮之后。Gmail 会用自己的内部协议链接 Gmail 的 Outgoing SMTP 邮件服务器。</p>
<p>Outgoing SMTP 验证用户权限,然后将邮件以 MIME 格式发送到发送队列中。</p>
<p>Gmail SMTP 服务器会通过 DNS 查询到域名 <code>qq.com</code> MXMail Exchanger 记录(<code>dig MX qq.com</code>),找到邮件服务器的 IP 所在。</p>
<p>在 Linux 下也可以通过 <code>dig mx qq.com</code> 来查询到。这一步在对应到自建的邮件服务器的时候,就是通过配置 DNS 的 MX 记录来实现的。</p>
<p>一般情况下会配置一个 A 记录 <code>mx.example.com</code> 指向服务器的 IP 地址。然后再配置一个 [[MX 记录]]<code>@</code> 全部域名的 MX 请求全部转发给 <code>mx.example.com</code></p>
<p><strong>2.SMTP 发送</strong></p>
<p>当 Gmail 的服务器找到 QQ 邮箱的 IP 地址之后,邮件就会通过 SMTPSimple Mail Transfer Protocol 协议连接服务器的连接,尝试发送给 QQ 的服务器。</p>
<p>为了简化理解SMTP 传输的时候就直接声明,我 <a href="mailto:a@gmail.com">a@gmail.com</a> ,我要发送邮件到 <a href="mailto:b@qq.com">b@qq.com</a> 内容是某某某。QQ 邮箱的服务器接收到 Gmail 的邮件之后,再根据用户名决定发给具体谁的邮箱。</p>
<p>这中间会发现不存在任何验证发送方身份的过程这也就意味着任何人都可以伪装一个任意的发送邮箱以一个伪装的邮箱发送邮件。SMTP 最早是建立在相互信任的基础之上的,所以也给后面的恶意使用留下了一些漏洞,为了修复这个漏洞发明了 SPF。</p>
<p><strong>3.SPF 验证</strong></p>
<p>上文提到过 SMTP 协议发送邮件的过程中没有验证发送方,这也就意味着发信方可以任意指定发件人邮箱地址,这会存在一些安全问题。</p>
<p>具体来说,本来我的 Gmail 邮箱是 <code>a@gmail.com</code>,假如有不法分子,就可以利用这个漏洞,伪装成自己是 <code>a@gmail.com</code> 给别人发送邮件。</p>
<p>[[SPF]] 的目的就是为了防止伪造发信人。</p>
<ul>
<li>SPF 的原理</li>
</ul>
<p>SPF 的实现原理非常简单,就是通过添加一条 DNS 记录。</p>
<p>如果邮件服务器收到一封来自主机 <code>1.1.1.1</code> 的邮件,并且发件人是 <code>a@gmai.com</code>,为了确认发件人,邮件服务器就会去查询 <code>gmail.com</code> 的 SPF 记录。如果域名设置了 SPF 记录,允许 <code>1.1.1.1</code> 的 IP 地址发送邮件,那么收件的邮件服务器就会认为邮件是合法的,否则就会退信。</p>
<p>有了 SPF 记录之后,如果有人想要伪装成 <code>a@gmail.com</code> 他既不能修改 gmail.com 的 DNS 解析,也无法伪造 IP 地址,就有效的防止了伪装。</p>
<ul>
<li>SPF 的语法 </li>
</ul>
<p>在自建邮件服务器的时候,经常会让我们设置一个 TXT 记录,配置值为 <code>v=spf1 mx ~all</code>,这表示的意思是允许当前域名的 MX 记录对应的 IP 地址。</p>
<p>下面再举个非常常见的例子:</p>
<pre style="background-color:#151515;color:#e8e8d3;"><code><span>v=spf1 a mx ip4:173.10.10.10 -all
</span></code></pre>
<p>表示允许当前域名配置的 A 记录MX 记录的 IP 地址,以及一个额外的 IP 进行发信。</p>
<ul>
<li>SPF 存在的问题 </li>
</ul>
<p>SPF 机制可以有效地规避了发送邮件方伪造发件人的问题。但实际使用的时候,如果你使用多个邮箱,然后设置了其中 <a href="mailto:c@163.com">c@163.com</a> 邮箱自动转发到 <a href="mailto:a@gmail.com">a@gmail.com</a> 中。</p>
<p>那么这个时候如果 <code>b@qq.com</code> QQ 邮箱发送了一封邮件到 <code>c@163.com</code> 邮箱163 邮箱原封不动地将邮件转发到 Gmail 邮箱,这个时候发件人是 <code>b@qq.com</code>,但是 Gmail 回去查询 qq.com 的 SPF 记录,但会发现并不包含 163 邮箱的 IP 地址,会误判转发的邮件;所以又诞生了 DKIM。</p>
<p><strong>4.DKIM</strong></p>
<p>DKIM (DomainKeys Identified Mail) 的缩写,允许发送者通过在邮件的 header 中包含一段数字签名来验证邮件。DKIM 使用公私密钥来确保邮件内容是从授信的邮件服务器发送的。</p>
<p>还是利用上面的例子,因为我们把所有发送到 163 邮箱的邮件都转发到了 Gmail 邮箱,所以来自 QQ 邮箱的邮件在验证 SPF 时会失败。</p>
<p>那么在 DKIM 中,发送邮件的服务器,比如 QQ 邮箱,会使用公私钥对邮件内容进行签名,并将签名和邮件内容一起发送。当 Gmail 收到从 163 邮箱转发过来的 QQ 邮箱邮件的时候,就会去查询 <code>qq.com</code> 的 DNS 记录,拿到公钥。然后使用公钥和签名来验证邮件内容。如果验签不通过,则将邮件判定为伪造。</p>
<p><strong>5.DMARC</strong></p>
<p>经过了 SPF 和 DKIM 的保证,是不是就可以完美的发送接收邮件了?其实并不能,我们通过邮件后台来看一下邮件的原始文本。</p>
<pre style="background-color:#151515;color:#e8e8d3;"><code><span>MIME-Version: 1.0
</span><span>Return-Path: xxx@fake.com
</span><span>DKIM-Signature: d=fake.com,b=adceabkekd12
</span><span>Date: Tue, 22 Mar 2022 06:37:58 +0000
</span><span>Content-Type: multipart/alternative;
</span><span> boundary=&quot;--=_RainLoop_587_997816661.1647931078&quot;
</span><span>From: admin@a.com
</span><span>Message-ID: &lt;a67d96a38592cdad46cca89e98dda26d@techfm.club&gt;
</span><span>Subject: Seems it works
</span><span>To: &quot;Somebody&quot; &lt;a@gmail.com&gt;
</span><span>
</span><span>
</span><span>----=_RainLoop_587_997816661.1647931078
</span><span>Content-Type: text/plain; charset=&quot;utf-8&quot;
</span><span>Content-Transfer-Encoding: quoted-printable
</span><span>
</span><span>~~
</span><span>
</span><span>----=_RainLoop_587_997816661.1647931078
</span><span>Content-Type: text/html; charset=&quot;utf-8&quot;
</span><span>Content-Transfer-Encoding: quoted-printable
</span><span>
</span><span>&lt;!DOCTYPE html&gt;&lt;html&gt;&lt;head&gt;&lt;meta http-equiv=3D&quot;Content-Type&quot; content=3D&quot;t=
</span><span>ext/html; charset=3Dutf-8&quot; /&gt;&lt;/head&gt;&lt;body&gt;&lt;div data-html-editor-font-wrap=
</span><span>per=3D&quot;true&quot; style=3D&quot;font-family: arial, sans-serif; font-size: 13px;&quot;&gt;&lt;=
</span><span>br&gt;~~&lt;signature&gt;&lt;/signature&gt;&lt;/div&gt;&lt;/body&gt;&lt;/html&gt;
</span><span>
</span><span>----=_RainLoop_587_997816661.1647931078--
</span><span>
</span></code></pre>
<p>SPF 解决了接收方验证发件人域名 SPF 记录内 IP 地址从而验证发件人的问题。但是因为 SPF 定义的发件人是 RFC5321 协议中规定的 Return-Path而 DKIM 在邮件头中直接包含了域名,只要使用该域名的公钥验证通过即可。</p>
<p>而现在的邮件服务给用户展示的发件人都是 From 字段,而不是 SPF 的 <code>Return-Path</code>,也不是 DKIM 的 <code>DKIM-Sginatur: d=</code>,所以攻击者可以通过伪造这两个字段,发送如上的邮件,完美通过 SPF 和 DKIM 检查,因为 SPF 检查 <code>Return-Path</code> 而 DKIM 验证的 <code>d=</code> 也是 <code>fake.com</code> 所以最终用户看到的发件人却是 <code>admin@q.com</code></p>
<p>所以就诞生了 DMARC。DMARC 结合了 SPF 和 DKIM规定了 <code>Return-Path</code> 和 <code>DKIM-Signature: d=</code> 两个至少需要有一个与 From 头对应,否则判定为失败。</p>
<p>当邮件服务器接收到邮件时,先验证 DKIMSPF然后再根据 DMARC 的配置检查。这样就能确保最终用户看到的 <code>From</code> 字段和 SPF、DKIM 认证的发件人一致了。</p>
</div>
<div class="pagination">
<div class="pagination__title">
<span class="pagination__title-h">Thanks for reading! Read other posts?</span>
<hr />
</div>
<div class="pagination__buttons">
<span class="button previous">
<a href="https://blog.dich.ink/mechrev-keyboard/">
<span class="button__icon"></span>&nbsp;
<span class="button__text">机械革命键盘失灵拯救记</span>
</a>
</span>
<span class="button next">
<a href="https://blog.dich.ink/how-email-works-2/">
<span class="button__text">电子邮件是如何工作的:POP3&#x2F;IMAP&#x2F;SMTP</span>&nbsp;
<span class="button__icon"></span>
</a>
</span>
</div>
</div>
</div>
</div>
<footer class="footer">
<div class="footer__inner">
<div class="copyright">
<span>©
2024
Dichgrem</span>
<span class="copyright-theme">
<span class="copyright-theme-sep">:: </span>
Theme: <a href="https://github.com/pawroman/zola-theme-terminimal/">Terminimal</a> by pawroman
</span>
</div>
</div>
</footer>
<script async defer src="http://173.249.208.93:12345/tracker.js" data-website-id="cluckwxwg0005qf4n55m737sz"></script>
</div>
</body>
</html>