一個從第一天就知道的資安問題,為什麼拖到客戶給我們一個 F 才修?
本篇是「誰在冒用你的 Domain 寄信」系列的第 1 / 4 篇。你可以從系列總覽開始閱讀,也可以直接接著看本文。
我接手公司主網域的維運時,它已經跑很多年了。email 的寄件認證——SPF、DKIM、DMARC——從草創期就沒設好。不是我搞砸的,是更早的「古人」留下的;那些人離職多久已經不可考,當初為什麼這樣設、有沒有什麼理由,也隨著他們一起消失了。
團隊其實「知道」這件事。它是那種會在閒聊時被提起、大家點點頭、然後沒有人把它寫進 sprint 的 folklore。它太低調了:不會讓服務掛掉、不會跳 alert、不會有人因為它半夜被 call。所以它就這樣躺著,躺了好幾年。
直到有一天,一個大客戶委託第三方資安評等廠商掃了我們,email 項目給了一個 F——百分位數幾乎墊底。
這篇想講的不是「怎麼設 DMARC」(網路上一堆)。而是兩件更有意思的事:為什麼一個大家都知道的問題能躺這麼久,以及我發現的——那個嚇人的 F,其實是你可以自己重現、自己量測的分數。
📎 系列文「誰在冒用你的 Domain 寄信」:這是系列起點(案例篇)。完全不熟 SPF/DKIM/DMARC?先看 #2 白話入門篇;想直接動手把 DMARC 推到
p=reject,看 #3 實戰 playbook;真的按下去那天發生什麼事,在 #4 執行日的假訊號。
背景:繼承來的、沒人排程的債
第三方資安評等(RiskRecon、BitSight、SecurityScorecard 這類)現在很常見。大客戶在簽約前或定期稽核時,會請這種廠商幫你「打分數」,然後把報告丟給你。一個對外可見的 F,在這種情境下特別刺眼——因為打分的是你的客戶。
但我接手的這個 email 認證問題有個特性:它是繼承來的。我沒設定它,我甚至不認識設定它的人。這種「孤兒 tech debt」最危險的地方在於——沒有人覺得自己該為它負責。新人覺得「這是以前就這樣」,老人走了,於是它變成一塊沒有 owner 的地。
發現過程:先別修,先搞懂它怎麼算的
我的第一反應本來是直接去改 DNS。但我停了一下,問自己一個問題:這個 F 到底是怎麼算出來的?
關鍵的領悟是:這類評等是 被動 OSINT(open-source intelligence)。報告裡白紙黑字寫著——它不碰你的系統、不打你的 API、不猜密碼。它只是從外面讀你的公開 DNS 記錄。
這跟大部分人的直覺相反。我第一次看到「被掃了」的時候,也以為對方在打我們的伺服器,還想說防火牆 log 裡應該找得到痕跡。並沒有——它連門都沒碰:
flowchart TB
subgraph MYTH["以為的:有人在掃我的系統"]
direction LR
A1["評等廠商"] -.->|"掃 port、打 API<br/>試弱密碼"| A2["你的伺服器"]
end
subgraph REAL["實際的:被動 OSINT,只讀公開資料"]
direction LR
B1["評等廠商"] -->|"dig"| B2["全球公開 DNS<br/>誰都查得到"]
B3["你,用同一支 dig"] -->|"dig"| B2
end
MYTH -->|"報告白紙黑字:<br/>不碰你的系統、不打你的 API<br/>你的 log 裡不會有任何痕跡"| REAL
B2 --> C["它讀得到的全部訊號<br/>SPF 結尾是 -all 還是 ?all<br/>DMARC 的 p= 是什麼<br/>DKIM 查不查得到公鑰"]
C --> D["加權加總,換算成 A 到 F"]
D --> E["所以:它看得到的,你也看得到<br/>這個 F 是可以自己重現的"]
也就是說,它看得到的東西,我用一支 dig 也看得到。於是我把它會看的訊號全查了一遍:
dig +short TXT example.com # SPF:發現是 "?all"(neutral,等於沒防護)
dig +short TXT _dmarc.example.com # DMARC:發現是 "p=none"(只監控、不執行)
dig +short TXT google._domainkey.example.com # 主要寄信的 DKIM:沒設
每一條都對得上報告的扣分點。既然如此,我乾脆寫了一支 60 行的 bash,用它報告裡列出的同一批訊號、自己配一組權重,算一個分數出來。
跑完——3/10,F。官方給的是 3.5。幾乎一模一樣。 廠商真正的加權演算法是專有的,我配的權重只是逼近——但至少我不用再等它下一份報告,就能自己量一次。
過程中還有兩個額外收穫:
- 報告會過時。 它的掃描日跟我讀它的那天差了兩週。我用
dig/openssl實測,發現有些被扣分的項目其實早就修好了。實測永遠比讀報告可靠。 - 恐慌是不必要的。 我把實際在寄信的管道一個個查清楚,發現真正高流量的寄信來源(交易信、行銷信)其實早就認證對齊了。缺口比 F 看起來小得多。
具體數據 / 結果
自製腳本 vs 官方分數:3/10 vs 3.5/10,差 0.5 分,等級同樣落在 F。足以拿來當免費、即時的對照工具。
email 認證的三個關鍵旋鈕(這就是評分的核心):
| 紀錄 | 弱(會被扣分) | 強(拿分) |
|---|---|---|
| SPF | ?all(neutral,不防偽) | -all(hardfail) |
| DMARC | p=none(只監控) | p=reject(強制) |
| DKIM | 主要寄件管道沒簽章 | 有簽章且對齊 |
同一件事換成腳本的算法,就是下面這張計分表。它也解釋了我們為什麼剛好卡在 3 分:
| 檢查項目 | 查法 | 配分 | 我們當時 |
|---|---|---|---|
| SPF 結尾機制 | dig +short TXT 網域 | -all 3 / ~all 2 / ?all 1 / 沒有 0 | ?all → 1 |
| DKIM 公鑰 | dig +short TXT google._domainkey.網域 | 查得到 1 / 查不到 0 | 沒設 → 0 |
| DMARC 政策 | dig +short TXT _dmarc.網域 | reject 5 / quarantine 3 / none 1 / 沒有 0 | p=none → 1 |
| MX 指向託管信箱 | dig +short MX 網域 | 是 1 / 否 0 | Google Workspace → 1 |
畫成格子更有感——滿分 10 格裡,DMARC 一筆 TXT 記錄就佔了 5 格:
可預期的修復軌跡:因為 DMARC 強制要分階段上線(先監控、再 quarantine、最後 reject),分數會一階一階往上爬,而不是一次到位:
flowchart LR
S0["現在<br/>SPF ?all · DKIM 沒設<br/>DMARC p=none<br/>3/10 F"]
S1["鋪設完成<br/>5/10 C"]
S2["先丟垃圾桶<br/>7/10 B"]
S3["完全強制<br/>10/10 A"]
S0 -->|"補 SPF、開 DKIM<br/>DMARC 加 rua 收報告<br/>零風險,不擋任何信"| S1
S1 -->|"p=quarantine<br/>盯著報告看 2 到 4 週"| S2
S2 -->|"p=reject<br/>SPF 收成 -all"| S3
那支腳本(去識別化版),你可以直接拿去量自己的網域:
#!/usr/bin/env bash
# email-auth-scorecard.sh — 用公開 DNS 重現「email 資安評等」分數
D="${1:-example.com}"
score=0
# ① SPF(0-3 分):撈出 TXT 裡 v=spf1 那一筆,只看結尾的 all 機制有多寬鬆
spf="$(dig +short TXT "$D" | tr -d '"' | grep -i 'v=spf1' | head -1)"
case "$spf" in
*"-all"*) score=$((score+3));; *"~all"*) score=$((score+2));;
*"?all"*) score=$((score+1));; esac
# ② DKIM(0-1 分):查得到公鑰就算有簽章。google 是 Google Workspace 的 selector,
# 換寄件管道要換名字(Mailgun 常見 mg、SendGrid 是 s1)
[ -n "$(dig +short TXT google._domainkey."$D")" ] && score=$((score+1))
# ③ DMARC(0-5 分):權重最重的一項,一筆 TXT 就佔掉滿分的一半
dmarc="$(dig +short TXT _dmarc."$D" | tr -d '"')"
case "$dmarc" in
*reject*) score=$((score+5));; *quarantine*) score=$((score+3));;
*p=none*) score=$((score+1));; esac
# ④ MX(0-1 分):有指向託管信箱,當作「這個網域真的在收信、有人在管」
dig +short MX "$D" | grep -qiE 'google|aspmx' && score=$((score+1))
echo "$D → $score/10"
3 分鐘上手:① 存成 email-auth-scorecard.sh ② chmod +x email-auth-scorecard.sh ③ ./email-auth-scorecard.sh yourdomain.com。回傳 0–10 分;想交叉驗證可再丟進 internet.nl、Hardenize、或寄封測試信到 mail-tester.com。
順帶一提:前置步驟(補 SPF、開 DKIM、DMARC 先開監控)其實零風險——它們只是「多授權」,不會擋掉任何信。唯一有風險的是最後把 DMARC 切到強制,而那步被監控期擋著。所以「F」聽起來嚇人,實際動工的爆炸半徑很小。
反思
技術面
第三方資安評等不是魔法——至少 email 這一項,它讀的就是任何人都查得到的那幾筆 DNS 記錄。一旦你知道它只讀 DNS,那個字母分數就從「客戶丟給你的判決書」變成「一個你能自己重現、自己追蹤的數字」。把黑箱變成儀表板,修起來才有對照、有信心。
心態面
- 繼承來的債最容易沒人修。 不是因為難,是因為沒有 owner。設定它的人走了,institutional knowledge 跟著走,剩下的人都覺得「這不是我弄的」。孤兒 tech debt 會一直是孤兒,直到出現一個逼它認領的理由。
- 「知道」不等於「會修」。 我們知道這問題很多年。讓它終於上線的不是內部的自覺,而是一個外部、可見、有人盯著的 forcing function——客戶的分數。這不太光彩,但很真實。
- 所以,與其等外部羞辱,不如自己動手讓債「可見」。 那支 60 行腳本最大的價值不是算分,是它把一塊抽象的、沒人理的 tech debt,變成主管會議上一句「我們現在 3/10,改完會到 10/10」。可見性,就是被排程的機率。
有趣發現
最諷刺的一點:我原本只是想搞懂報告怎麼算,結果「自己重現分數」這件事,意外成了整個專案最有效的溝通工具。一份 20 頁的 PDF 很難推動排程;但一個會跳動的數字、一條看得到終點的軌跡,讓「修這個」第一次變得有吸引力。
技術債從來不缺「知道的人」,缺的是讓它被看見的人。
如果讀到這裡,還是不太確定 SPF、DKIM、DMARC 各自在管什麼,系列 #2 白話入門篇 用「寄一封實體掛號信」的比喻把三個一次講完;已經懂了、想直接動手把 p=none 推到 p=reject,接 #3 實戰 playbook;想先看看真的按下去那天會遇到什麼,看 #4 執行日的假訊號。