通俗解釋反偽造郵件機制 SPF、DKIM 和 DMARC

Wi1dcard發表於2021-02-03

這篇部落格一改我之前文風的常態,因為網上的相關專業資料已經很多,我儘可能以通俗易懂的語言來介紹,放棄了部分專業性,望讀者見諒。

歡迎關注我的技術部落格:wi1dcard.dev

我們先來談談「防止攻擊者偽造我們向其他人傳送郵件」。要理解為什麼會存在偽造郵件以及如何防止偽造郵件,首先需要明白郵件機制是怎樣工作的。

傳送郵件時發生了什麼?

為了方便理解,你可以把「傳送郵件」看作是類似提交 HTTP 請求的過程。

舉個例子,假設我們是 user@foo.com,給 remote@bar.com 發郵件,在點選「傳送」的那一刻,我們的郵件伺服器查詢 bar.com 的 MX 記錄,DNS 伺服器返回 1.1.1.1,接著我們的郵件伺服器與對方 1.1.1.1 建立 TCP 連結,傳送以下內容(僅供理解概念,與真實情況不同):

你好,我是 <user@foo.com>
我有郵件給 <remote@bar.com>
標題是 xxxx
內容是 yyyy

於是我們的郵件就到達了 remote@bar.com 的郵件伺服器,伺服器記錄並儲存到收件箱供對方檢視。

這整個過程中不存在任何對「我方」的身份驗證,也就是說,任何人可以連結到 remote@bar.com 的郵件伺服器,說:我是 <user@foo.com>,我有郵件給 xxxx。這就給了攻擊者偽裝成「我方」的可能性。

SPF

怎麼辦呢?於是 SPF 被發明瞭。SPF 機制要求我們,在需要傳送郵件的域名下(foo.com),新增一條 SPF 記錄,內容大概是(僅供理解概念,與真實情況不同):

我方郵件伺服器 IP 地址是 = 2.2.2.2, 3.3.3.3

再次重複上文郵件傳送過程,對方郵件伺服器收到我方郵件(來自 foo.com)時,就可以查詢 foo.com 的 DNS 記錄,找到我們提前設定好的 SPF 記錄。同時,因為「傳送」的過程實際上就是 TCP 建立連結,因此對方伺服器也能夠得到傳送者的伺服器 IP。把 傳送郵件的伺服器 IPSPF 記錄內的 IP 地址 進行對比,如果不匹配,則認為是偽造郵件。

可以看出,通過 SPF 機制,我們能達到「不是從我們伺服器 IP 地址發出的郵件,被視為垃圾郵件」的效果。但是,SPF 存在一些侷限性,例如:

假設你有兩個郵箱 jane@company.comjane@personal.com,你特別熱愛工作,設定了所有發到 jane@company.com 的郵件全部轉到 jane@personal.com

此時一封來自 user@foo.com(配置了 SPF)的郵件傳送到了 jane@company.com,郵件伺服器根據你的設定、原封不動地 轉發該郵件到 jane@personal.com

jane@personal.com 的郵件伺服器收到郵件後,開始核對 SPF 記錄。這時 郵件的發件人依然是 user@foo.com,根據查詢 foo.com 的 SPF 配置內並不包含 company.com 郵件伺服器的 IP 地址。因此該郵件將被誤判為偽造郵件。

DKIM

儘管如今許多郵件服務商在自動轉發郵件時,會修改郵件的發件人來規避 SPF 規則限制。但這在理論上並不「優雅」,於是又誕生了 DKIM。

DKIM 要求我們在我方郵件伺服器內生成一組金鑰對,私鑰儲存在伺服器內,公鑰通過 DNS 記錄公開。一樣,新增一條 DNS 記錄,內容大概是(僅供理解概念,與真實情況不同):

我方郵件簽名公鑰是 = 0xnoirh22h381uj2eoqdsjlaisud

再次重複上文郵件傳送過程,與 SPF 不同,我方伺服器在傳送郵件時,需要對郵件內容使用私鑰簽名,並將簽名與郵件內容一起攜帶著傳送給對方伺服器。對方收到我方郵件時(來自 foo.com),同樣查詢 foo.com 的 DNS 記錄,拿到公鑰。使用公鑰和簽名驗證郵件內容,如果驗籤不通過,則將郵件判定為偽造。

結合以上例子(user@foo.com -> jane@company.com -> jane@personal.com),當 jane@company.com 自動轉發郵件時,無需修改任何欄位(也不能修改,不然驗籤就會失敗),jane@personal.com 收到郵件後查詢 foo.com 的 DKIM 公鑰,驗證郵件簽名通過,即可認為是正常郵件。

如此一來,結合 DKIM 和 SPF,看起來萬事大吉了?並沒有。讓我們更加深入一點,看個稍微真實一點的郵件。

Return-Path: user@fake.com
DKIM-Signature: d=fake.com, b=aldjiopurn1o23u108923jiuq123jdal
From: <user@foo.com>
To: <remote@bar.com>

剛剛提到,基於 SPF,收件方能夠對比「發件伺服器 IP 地址」與「發件人域名的 SPF 記錄內的 IP 地址」。然而,因為郵件系統發展歷史久遠,SPF 定義的「發件人」是 RFC5321.MailFrom 規定的 Return-Path。DKIM 則更是在郵件頭裡直接攜帶了域名(DKIM-Signature: d=),只要使用該域名的公鑰驗證通過即可。

然而,如今郵件服務給終端使用者展示的「發件人」幾乎都是 From 欄位,並非 SPF 的 Return-Path,也不是 DKIM 的 DKIM-Signature: d=。也就是說,攻擊者(fake.com)可以傳送以上這封郵件,可以完美通過 SPF 和 DKIM 檢查(因為 SPF 驗證的 Return-Pathfake.com,而 DKIM 驗證的 d= 也是 fake.com),而終端使用者看到的發件人卻是 From 內的 user@foo.com。這顯然是不可接受的。

DMARC

又於是,DMARC 誕生了。

DMARC 並沒有規定具體的「驗證措施」,而是基於 SPF 和 DKIM(或二者之一)。它規定,SPF 的 Return-Path 或者 DKIM 的 DKIM-Signature: d= 二者至少需有其一與 From 頭對應,否則被判定為 fail。這個過程叫做 Identifier Alignment。同時,DMARC 還提供了大量的增強功能,比如指定那些 fail 的郵件是進入 Spam 還是直接拒收。配置 DMARC 的方式與 SPF 和 DKIM 類似,在我方域名 DNS 內新增 TXT 記錄,內容類似這樣(僅供理解概念,與真實情況不同):

70% 的 fail 郵件進入 Spam;
郵件服務商應當將每天 fail 的郵件彙總後傳送到 xxx@foo.com 以供分析;
嚴格匹配,不允許 Return-Path 是 From 的自域名;

當對方郵件伺服器收到郵件時,即可先驗證 DKIM、SPF,再根據 DMARC 的配置,檢查 Identifier Alignment、決定具體措施等。

如此一來,能夠確保最終收件人使用者看到的 From 與 SPF、DKIM 認證的發件人一致,且經過了 SPF 或 DKIM 認證。這樣,攻擊者也就無法偽造我們發出的郵件了。

結語

總結以上過程,可以看出:如果想要徹底避免偽造郵件,是需要收發雙方協作的過程。除了發件方要配置好 SPF 和 DKIM 供收件方驗證外,收件方的伺服器也必須支援 對收到的郵件進行 SPF 和 DKIM 認證

當我們作為收件方時,能做的就是儘可能使用支援 SPF 和 DKIM 的郵件服務商,例如 Gmail;避免使用不支援這些機制的服務商,比如阿里雲 / 釘釘郵箱。

本作品採用《CC 協議》,轉載必須註明作者和本文連結
Former WinForm and PHP engineer. Now prefer Golang and Rust, and mainly working on DevSecOps and Kubernetes.

相關文章