先讀範圍,再決定郵件解析結果能怎麼用。
郵件批次解析器只在你的瀏覽器內處理 .eml、.mbox 與 .emlx,不上傳檔案、不渲染郵件內容、不使用瀏覽器儲存空間。本頁說明做什麼、不做什麼、怎麼取得原始郵件、每一項特徵提示的意思、實測規模、與 Python 的交叉驗證,以及如何自行驗證。
先說清楚這不是什麼
這個工具只整理「郵件裡寫著什麼」。它不判斷哪一封是偽造、詐騙或釣魚,不是鑑識鑑定,也不是法律意見。
- 不驗證 DKIM 與 SPF 簽章。驗證 DKIM 需要寄件網域公開的金鑰(DNS 查詢)與原始、未被改動的郵件;驗證 SPF 需要寄信當下的伺服器 IP 與 DNS。這個工具離線運作,兩者都沒有。頁面上的「驗證結果」只是轉述郵件裡 Authentication-Results、Received-SPF 標頭寫著的文字。
- 不做 IP 地理定位。需要外部資料庫,而且 IP 的所在地常常與寄件人的所在地無關(雲端、VPN、代發)。工具只標示 IP 的類別,例如文件用保留位址、私有網段、本機迴路。
- 不渲染郵件內容。HTML 郵件只被掃描,不放進頁面、不執行;圖片、樣式表與追蹤像素一律不載入。網址以 defang 形式(hxxp、[.])顯示且不可點擊,需要時按「複製原網址」。
- 特徵提示不是結論。每一項都是「這個特徵常見於偽冒郵件,但也可能出現在正常郵件(例如轉寄、郵件群組、客戶系統代發)」。沒有任何提示也不代表郵件可信。
- 標頭可以被改。寄件人、日期、Message-ID 都由寄件端的軟體填寫;匯出、轉寄、手動編輯都會改動標頭。
做什麼、不做什麼
- 做什麼
- 讀入 .eml(一檔一封)、.mbox 與 .mbx(一檔多封)、Apple Mail 的 .emlx,可多選;也可貼上標頭或原始碼。
- 每封郵件解析寄件人、收件人、副本、Reply-To、Return-Path、主旨(含 RFC 2047 與 RFC 2231 編碼)、日期(轉成 ISO 與 UTC)、Message-ID、In-Reply-To、References、Received 路徑、驗證結果標頭(原文轉述)、MIME 結構、內文文字(依 charset 解碼)、附件(解碼後大小與 SHA-256、依檔頭判斷內容)、連結(含包裝網址解出、同形異字與顯示文字不一致的標示)。
- 清單支援搜尋(寄件人、收件人、主旨、附件檔名;內文搜尋較慢,可取消)、依附件、特徵提示、日期區間、寄件網域、來源檔篩選,並可依日期、寄件人、主旨、大小、附件數、指標數排序;可切換成「對話串」(先看 References 與 In-Reply-To,再看正規化後的主旨)或「時間軸」(以各郵件換算成 UTC 的時間點排序,時區已換算)。
- 十萬封以上的清單以虛擬捲動呈現,畫面只畫看得到的幾十列。
- 匯出 CSV、JSON(可選是否含內文)、單封 TXT 報告、全部附件雜湊 CSV;可把單封摘要送到「AI 偵探實驗室」(需要你勾選確認才會送出)。
- 顯示每個來源檔的整檔 SHA-256(分塊讀取,自行實作的增量 SHA-256)。
- 不做什麼
- 不上傳、不聯網、不使用任何瀏覽器儲存空間(含 cookie);關閉或重新整理頁面,畫面上的內容就消失。
- 不驗證簽章、不做 IP 定位、不解密、不執行或渲染任何郵件內容。
- 不修改原檔。匯出的 CSV、JSON、報告都是新檔案。
- 不評分、不判斷真偽,也不替任何程序背書。
怎麼取得原始郵件
各家郵件軟體的選單名稱會隨版本改變,以下是「通常」的位置;找不到時,請以你使用的版本的說明為準。重點是取得「原始碼」(含 Received、From、Subject 等標頭),而不是另存的網頁、列印出的 PDF 或截圖。
- Gmail(網頁版)
- 通常在郵件右上角「⋮ 更多」選單中的「顯示原始郵件」;那個頁面可以「下載原始郵件」存成 .eml,或「複製到剪貼簿」後貼到本頁的貼上區。
- Outlook
- 新版與網頁版通常可在郵件的「⋯」選單找到「檢視」→「檢視郵件來源」或「下載」;傳統桌面版通常在「檔案 → 內容」可複製「網際網路標頭」(只有標頭,沒有內文)。桌面版另存的 .msg 本工具不支援;能另存成 .eml 的版本請存成 .eml,不能的請改貼標頭。
- Apple Mail
- 通常在選取郵件後的「顯示 → 郵件 → 原始碼」(或「完整標頭」);也可以把郵件拖曳到桌面存成 .eml。信箱資料夾裡的 .emlx 檔本工具可直接讀取。
- Thunderbird
- 通常在郵件的「更多 → 檢視原始碼」(快捷鍵常是 Ctrl/⌘ U),或「檔案 → 另存為 → 檔案」存成 .eml。Thunderbird 的本機資料夾本身就是 mbox 格式(沒有副檔名的檔案),可複製出來直接選取;請先關閉 Thunderbird 再複製,避免讀到寫到一半的檔案。
- 其他
- 許多郵件系統可匯出整個信箱成 .mbox;匯出時請確認格式是 mbox(不是 pst、ost 或 zip)。
四個步驟
- ① 選擇或貼上。選檔或貼上,按「開始解析」。大檔會先掃一遍找郵件分隔行並同時計算整檔 SHA-256,再分批逐封建立索引;處理中有進度,可隨時取消,取消後保留已建立的部分。
- ② 清單與摘要。先看摘要(郵件數、日期範圍、附件、特徵提示統計、各來源檔的狀態);再用搜尋、篩選、排序縮小範圍。清單取得焦點後可用方向鍵、PageUp、PageDown、Home、End 移動,Enter 看細節。日期後面的「*」表示該封沒有可解析的 Date,改用最後一筆 Received 的時間排序。
- ③ 單封細看。七個分頁:摘要與特徵、標頭、郵件路徑、內文、附件、連結、原始碼(超過上限只顯示前面的部分,可下載 .eml)。分頁可用左右方向鍵切換。
- ④ 匯出。CSV、JSON、全部附件雜湊、單封報告、送到線索推理艙。
特徵提示:每一項的意思與常見的正常原因
每一項都是「這個特徵常見於偽冒郵件,但也可能出現在正常郵件(例如轉寄、郵件群組、客戶系統代發)」。工具只列出事實與依據,不下結論;「注意」只表示值得你再多看一眼,不表示這封信有問題。
| 特徵 | 等級 | 意思 | 常見的正常原因 |
|---|---|---|---|
| 寄件人網域與 Return-Path 網域不同 from-returnpath | 資訊 | Return-Path 是退信要送回的地址(信封寄件人)。多數一般往來信件,兩者的網域相同或相近。 | 電子報與行銷寄信平台、郵件群組、客戶系統代發、轉寄服務,常用自己的網域接收退信。 |
| Reply-To 網域與寄件人網域不同 from-replyto | 注意 | 按下「回覆」時,信會寄到 Reply-To 的地址,而不是寄件人地址。寄件人看起來正常、回覆卻轉往別處,是偽冒郵件常見的做法。 | 客服系統、網站表單通知、祕書代發、郵件群組常會這樣設定。 |
| Message-ID 網域與寄件人網域不同 from-msgid | 資訊 | Message-ID 是產生這封信的系統給的編號,通常帶有該系統的網域。 | 使用大型信箱服務、寄信平台、轉寄或客戶系統代發時很常見。 |
| DKIM 簽章網域(d=)與寄件人網域不一致 from-dkim | 資訊 | 信裡有 DKIM 簽章,但簽章者聲明的網域與寄件人網域不同。這只是信裡寫的內容,本工具沒有驗證簽章。 | 寄信平台與郵件群組常用自己的網域簽章;子網域與主網域的搭配也算常見。 |
| 顯示名稱含有另一個電子郵件地址 name-email | 注意 | 顯示名稱可以寫成任何文字。有些郵件軟體只顯示名稱,若名稱看起來像一個地址,收件人可能誤以為那就是寄件地址。 | 有些系統把自己的客服信箱放進顯示名稱;名稱裡的地址與實際寄件地址相同時不列入。 |
| SPF 驗證結果不是通過 auth-spf | 注意 | SPF 檢查寄信伺服器的 IP 位址,是否在寄件網域公告的允許名單內。這是收件伺服器寫在信裡的結果,本工具沒有重新驗證。 | 轉寄、郵件群組、自動轉發規則會讓 SPF 失敗;softfail 與 none 也常見於尚未完整設定的網域。 |
| DKIM 驗證結果不是通過 auth-dkim | 注意 | DKIM 是寄件網域加在信上的數位簽章,用來確認標頭與內文沒有被改過。這是收件伺服器寫在信裡的結果,本工具沒有重新驗證。 | 郵件群組與轉寄程式會改動內文或標頭,造成簽章失敗;許多小型網域沒有設定 DKIM(none)。 |
| DMARC 驗證結果不是通過 auth-dmarc | 注意 | DMARC 檢查寄件人網域是否與通過 SPF 或 DKIM 的網域一致,並依網域擁有者的政策處理。這是收件伺服器寫在信裡的結果,本工具沒有重新驗證。 | 轉寄與郵件群組常讓 DMARC 失敗;沒有設定 DMARC 的網域會顯示 none。 |
| 信裡沒有 SPF/DKIM/DMARC 的驗證結果標頭 auth-missing | 資訊 | 沒有 Authentication-Results 也沒有 Received-SPF 標頭,無法從信裡讀到收件伺服器的驗證結論。 | 標頭被刪減、只貼了部分標頭、從舊式軟體匯出、公司內部信件、手動編輯過的 .eml 常缺這些標頭。 |
| 寄件日期在未來 date-future | 注意 | Date 標頭的時間比現在(這台裝置的時鐘)晚超過一天。 | 寄件者的電腦時鐘不準、時區寫錯,或這是排程寄出的信;你這台裝置的時鐘不準也會造成。 |
| 寄件日期晚於最早的收信紀錄超過 1 小時 date-after-received | 注意 | Date 由寄件者的軟體填寫,Received 由伺服器加上。信不可能在寄出之前就被伺服器收到。 | 寄件者電腦時鐘錯誤、時區標示錯誤;伺服器之間時鐘不同步。 |
| 寄件日期比最早的收信紀錄早超過 24 小時 date-before-received | 資訊 | 信標示的寄出時間,與最早一台伺服器收到它的時間相隔一天以上。 | 草稿稍後才送出、寄件端佇列延遲、伺服器停機後補送,或匯出時改過標頭。 |
| 沒有 Date 標頭,或日期無法解析 no-date | 資訊 | 正常的郵件軟體都會填寫 Date。 | 某些自動產生的系統信件、手動編輯過的 .eml 會缺 Date;日期格式不標準時也會解析失敗。 |
| 沒有 Message-ID no-message-id | 資訊 | 郵件軟體或寄信伺服器通常會加上 Message-ID 作為這封信的編號。 | 某些簡易程式寄出的通知信、被刪減標頭的檔案會沒有。 |
| 重複的 From、Subject 或 Date 標頭 dup-headers | 注意 | 同一個標頭出現兩次以上時,不同軟體可能採用不同的那一個,有人利用這點讓畫面顯示的與被驗證的不一致。 | 少數舊式程式、轉寄程式或匯出工具會重複寫入標頭。 |
| 顯示名稱含雙向控制字元或零寬字元 bidi-name | 注意 | 這些字元看不見,卻能改變文字的顯示順序,或讓兩個看起來一樣的名稱其實不同。 | 阿拉伯文、希伯來文的名稱可能合法使用方向標記;複製貼上有時也會帶入零寬字元。 |
| 主旨含雙向控制字元或零寬字元 bidi-subject | 資訊 | 這些字元看不見,卻能改變文字的顯示順序,或讓文字比對失效。 | 阿拉伯文、希伯來文的主旨可能合法使用方向標記;複製貼上有時也會帶入零寬字元。 |
| 寄件人欄位有多個地址 from-multiple | 資訊 | From 有多個地址時,規格要求再加上 Sender 標頭;一般個人信件很少這樣。 | 多人共同署名的系統通知、部分群組信件。 |
| 沒有可解析的寄件人(From) no-from | 注意 | 沒有 From,或內容無法解析成地址。 | 被刪減標頭的檔案、格式特殊的系統信件。 |
| 附件是可執行檔類型 att-executable | 注意 | 可執行檔(例如 .exe、.msi、.dll)開啟後會在電腦上執行程式。本工具不會執行或預覽任何附件。 | IT 部門或廠商傳送的安裝程式;一般往來信件較少以這類型傳送。 |
| 附件是腳本類型 att-script | 注意 | 腳本檔(例如 .js、.vbs、.ps1、.bat)開啟後會執行其中的指令。 | 工程師之間交換的小工具;一般往來信件較少以這類型傳送。 |
| 附件是捷徑類型 att-shortcut | 注意 | 捷徑(.lnk、.url)可以指向任何程式或網址,檔案本身很小,看不出它會做什麼。 | 偶爾有人用 .url 傳送網站書籤。 |
| 附件是磁碟映像類型 att-diskimage | 注意 | 磁碟映像(.iso、.img、.vhd)開啟後會掛載成一個磁碟,裡面的檔案可能不會被某些防護軟體檢查。 | 軟體光碟映像、虛擬機器檔案的傳送。 |
| 附件是可含巨集或內嵌可執行內容的文件 att-macro | 注意 | 巨集啟用的 Office 文件(.docm、.xlsm 等)與 OneNote 檔案可以內嵌會自動執行的內容。 | 企業內部用巨集做報表的文件。 |
| 附件是網頁或向量圖檔 att-web | 注意 | .html、.htm、.svg 這類檔案可以內含腳本,離線打開時也會執行,並可顯示假的登入畫面。本工具只把它們當文字或位元組處理,不會開啟。 | 網站設計稿、報表匯出、收據與發票的網頁版。 |
| 附件是壓縮檔 att-archive | 資訊 | 壓縮檔裡面的檔案無法在這裡檢視。常見的處理方式是在隔離環境解壓縮後再檢查。 | 一般文件打包傳送。 |
| 壓縮檔標示為加密 att-archive-encrypted | 注意 | 加密的壓縮檔無法被郵件閘道掃描內容,因此有人用它夾帶不想被檢查的檔案。這裡只讀 ZIP 首個項目的標記。 | 傳送機敏文件時以密碼保護,是正常的安全作法。 |
| 附件檔名有雙重副檔名或副檔名被空白遮住 att-double-ext | 注意 | 像 invoice.pdf.exe 這種檔名,在只顯示前半段的畫面上看起來像文件,實際是另一種類型。 | 含有句點的正常檔名,例如 report.v2.docx(最後一段仍是文件類型時不列入)。 |
| 附件內容是可執行檔,檔名或宣告類型卻不是 att-mismatch-exec | 注意 | 從檔案開頭的位元組判斷是可執行檔,但檔名或 Content-Type 宣告成別的類型。 | 極少見;多半是刻意改名,也可能是判斷用的位元組巧合。 |
| 附件內容與副檔名或宣告類型不相符 att-mismatch | 資訊 | 從檔案開頭的位元組判斷出來的類型,與副檔名或 Content-Type 不一致。 | 把圖片改了副檔名、郵件軟體誤標類型、檔案格式有多種變體。 |
| 附件檔名含雙向控制字元或零寬字元 att-bidi-name | 注意 | 例如用 U+202E 讓 invoice_fdp.exe 在畫面上顯示成 invoice_exe.pdf 的樣子。 | 含阿拉伯文、希伯來文的合法檔名可能含方向標記。 |
| 只有 HTML 版本,且含遠端內容 html-only-remote | 資訊 | 這封信沒有純文字版本,HTML 內有指向外部網站的圖片或資源;在郵件軟體中打開時,可能會對外連線並讓對方知道你開了信。本工具不渲染 HTML,也不載入任何遠端資源。 | 電子報、帳單通知、行銷信幾乎都是這樣。 |
| 疑似追蹤像素或帶識別參數的遠端圖片 tracking-pixel | 資訊 | 1×1 或隱藏的遠端圖片,或網址帶有看似個人識別碼的參數,常用來偵測信件是否被開啟。 | 行銷信與電子報的開信統計。 |
| 連結的顯示文字像是網址,實際連到別的網域 link-text-mismatch | 注意 | 畫面上看到的文字是一個網址或網域,但點下去會連到另一個網域。 | 追蹤用的轉址(寄信平台改寫網址)、網址保護服務改寫連結。 |
| 連結使用短網址服務 link-shortener | 資訊 | 短網址無法從網址本身看出最後會連到哪裡。 | 社群與行銷信常用;LINE 官方帳號邀請連結也使用短網址。 |
| 連結在主機名稱前帶有「帳號@」 link-userinfo | 注意 | 像 https://bank.example@other.example/ 這種寫法,看起來像在 bank.example,實際連到的是 @ 後面的主機。 | 現代網站幾乎不會這樣用;舊式的 FTP 連結偶爾會帶帳號。 |
| 連結的主機是 IP 位址 link-ip-host | 注意 | 正常的服務通常使用網域名稱;直接用 IP 位址連線的網頁,無法從名稱判斷是誰的。 | 公司內部系統、路由器或監控設備的管理介面、測試環境。 |
| 連結的網域混用了不同文字系統 link-mixed-script | 注意 | 例如拉丁字母夾雜外觀相同的西里爾字母,網址看起來與知名網站一樣,實際是不同的網域。 | 中文、日文、韓文與英數字母搭配(例如「台灣abc」「日文漢字加假名」)本來就常見,不列入;少數品牌名稱會刻意混用。 |
| 連結的網域是國際化網域(xn--) link-punycode | 資訊 | 網域名稱使用了非英文字元,在網址列可能顯示成看起來像英文的樣子。 | 使用中文、日文、歐洲語言字元的合法網站。 |
| 連結異常地長 link-long | 資訊 | 超過 300 個字元的網址,多半夾帶識別碼、轉址參數或編碼過的內容。 | 寄信平台的追蹤連結、雲端文件分享連結。 |
| 連結使用 javascript:、data: 等非一般網頁協定 link-unsafe-scheme | 注意 | 這類連結不是前往網頁,而是執行腳本或內嵌內容。 | 極少見;某些郵件範本的預設佔位連結。 |
| 郵件路徑中有時間倒退 path-backwards | 資訊 | 某一跳的時間比前一跳還早,表示其中有伺服器時鐘不準,或有紀錄被加過、改過。 | 伺服器之間時鐘差幾分鐘到幾小時並不少見;時區標示錯誤也會造成。 |
| 郵件路徑中有超過 24 小時的間隔 path-gap | 資訊 | 信在某兩跳之間停留超過一天。 | 收件伺服器暫停、灰名單重試、郵件佇列積壓、轉寄規則延遲。 |
郵件路徑與驗證結果的限度
- Received 只有一部分可信。每台伺服器在郵件上面加一筆 Received,由下往上是由早到晚。只有「你信任的那台收信伺服器」寫的那一筆,以及它之後(上方)寫的才可靠;它之前(下方)的紀錄,寄件者可以事先偽造。工具的路徑表由最早排到最近,只是照信上寫的排,沒有辦法判斷哪一筆是真的。
- 匯出會損失標頭。用「轉寄為附件」、截圖、列印、另存成 PDF,或某些軟體匯出 .eml 時,可能刪掉部分標頭,也可能新增標頭;只貼上「完整標頭」而不含內文時,無法核對內文的雜湊與簽章。
- 轉寄會改變標頭。轉寄(尤其是郵件群組、自動轉寄規則)會讓 SPF 失敗、讓 DKIM 簽章因內文被改而失敗、改寫 From 或加上 Reply-To。這些「失敗」常常是正常現象。
- 時間差只是時鐘之差。各伺服器時鐘不一定同步,時間差不是精確的傳輸時間。
- IP 只標類別。路徑中的 IP 只標示是否為文件用保留位址、私有網段、本機、連結本地等,不查所屬網路與地理位置。
匯出格式
- CSV(UTF-8,開頭有 BOM,欄位以逗號分隔、CRLF 換行)
- 欄位:序號、來源檔、日期ISO、日期原文、寄件人、收件人、副本、主旨、Message-ID、對話串、附件數、附件名與SHA-256、SPF結果、DKIM結果、DMARC結果、指標數、指標名稱、Return-Path、Reply-To、位元組數、來源位置、郵件SHA-256。日期欄為 ISO 8601(帶郵件自己的時區偏移),另有原文欄;儲存格以 = + - @、Tab 或 CR 開頭時,前面加一個單引號,避免試算表當成公式執行(該格內容因此多一個字元)。
- 全部附件雜湊(CSV)
- 欄位:序號、來源檔、郵件主旨、附件位置、角色、附件名、宣告類型、內容判斷、解碼後位元組數、SHA-256、旗標。雜湊是附件解碼後內容的 SHA-256。
- JSON
- 最上層有 generator、generatedAt、note(本工具的聲明)、sources(來源檔與 SHA-256)、messages(每封的欄位、驗證結果原文、路徑、附件、連結、特徵提示與依據)。勾選「附上內文」才會包含內文文字。網址一律 defang。
- 單封 TXT 報告
- 來源、摘要、特徵提示(含意思與常見的正常原因)、郵件路徑、附件、連結(defang)、解析註記與聲明;可選擇附上內文節錄。
- 送到線索推理艙
- 只送出步驟 ③ 選取的那一封的摘要文字(可選是否附內文節錄,最多 2000 字),先顯示預覽,勾選確認後才能送出;送出會開啟「AI 偵探實驗室」分頁,資料只在你的瀏覽器內從這個分頁傳到那個分頁。失敗時會說明原因,並建議改用「複製預覽文字」。
規模與實測
讀檔以 8 MiB 為單位分塊。mbox 分兩段:先掃分隔行並同時計算整檔 SHA-256,再分批(每批約 16 MiB)逐封建立精簡索引;詳細內容只在你點開那一封時才重新解析。索引與解析在背景 Worker 進行,主畫面不被卡住。上限:單一 mbox 檔 2 GiB(見下方實測)、全部來源合計 20 萬封、至多 5000 個來源檔、單封 128 MiB;超過時會明確標示「只處理前面的部分」,不會悄悄截斷。瀏覽器記憶體吃緊時,工具會停止建立索引並保留已建立的部分。
怎麼讀這些數字:全部是「本機沙箱小樣本」:合成資料(隨機位元組附件、虛構網域),測試機只有 2 核心,量測時還有其他工作同時在跑,時間有雜訊,只能當量級參考;不同的瀏覽器、裝置、郵件內容(真實郵件的附件與 HTML 通常更大、更複雜)結果會不同。這裡沒有量到的組合,就不承諾。
| 環境 | 檔案大小 | 郵件數 | 索引用時(秒) | 記憶體峰值(MiB) | 備註 |
|---|---|---|---|---|---|
| Node 22(核心單獨,無畫面) | 77.6 MiB | 5,000 | 4.0 | 178 | 含整檔 SHA-256;之後排序 2 毫秒、主旨搜尋 25 毫秒、內文搜尋 0.6 秒 |
| Node 22(核心單獨,無畫面) | 382.4 MiB | 50,000 | 20.3 | 326 | 含整檔 SHA-256;之後排序 17 毫秒、主旨搜尋 90 毫秒、內文搜尋 4.7 秒 |
| Node 22(核心單獨,無畫面) | 1912.2 MiB | 200,000 | 75.7 | 797 | 郵件數達上限 20 萬封而停止,狀態標為「部分」 |
| 瀏覽器(Chromium 無頭,CPU 降速 4 倍) | 77.6 MiB | 5,000 | 7.2 | 875 | 從按下「開始解析」到清單可用;RSS 是整個瀏覽器所有行程的加總(含共用記憶體重複計算),只供參考 |
| 瀏覽器(Chromium 無頭) | 382.4 MiB | 50,000 | 21.2 | 1074 | 從按下「開始解析」到清單可用;RSS 是整個瀏覽器所有行程的加總(含共用記憶體重複計算),只供參考 |
| 瀏覽器(Chromium 無頭) | 764.8 MiB | 100,000 | 40.2 | 1186 | 從按下「開始解析」到清單可用;RSS 是整個瀏覽器所有行程的加總(含共用記憶體重複計算),只供參考 |
| 瀏覽器(Chromium 無頭) | 1912.2 MiB | 200,000 | 88.5 | 1426 | 從按下「開始解析」到清單可用;RSS 是整個瀏覽器所有行程的加總(含共用記憶體重複計算),只供參考 |
- 2 GiB 上限的依據:實測過的單檔大小是 1912.2 MiB(約 1.87 GiB)的 mbox,在 Chromium 與 Node 都能完成(因郵件數達上限 20 萬封而停在「部分」)。超過 2 GiB(2048 MiB)的檔案,頁面會直接拒絕並說明;沒有測試過接近 2 GiB 但郵件很少(每封很大)的情況,也沒有在其他瀏覽器或手機上測過這個大小。
- 手機:未在真實手機上測大檔。手機的記憶體遠小於桌機,建議只處理小檔;記憶體不足時工具會停止並保留已建立的部分,但無法承諾每種裝置都能撐到那一步。
- 內文搜尋需要逐封重新解析,比索引慢很多(見上表備註),所以做成要按鈕、可取消、有進度。
- 清單 10 萬列的虛擬捲動、鍵盤選取、排序、搜尋都在上述瀏覽器實測中通過(見 QA 資料夾的端對端結果)。
與 Python email 套件的交叉驗證
先講限制:語料是自行編寫的合成郵件(共 449 封、25 個類別;網域只用 RFC 2606 保留名稱),不是從真實信箱取樣,不代表真實世界的分布;其中不少案例是照 Python 的行為方式建構的,對 Python 有「主場優勢」。「已記錄差異」是逐項人工看過、判定有理由才標記的,所以「排除已記錄差異後的一致率」帶有人為判斷的成分,請以嚴格一致率為主。
- 比對對象:Python 3.13.16 的
email套件(policy.default)與本工具,逐欄位比對:標頭、主旨、寄件人、收件人、副本、Reply-To、日期、Message-ID、MIME 結構、純文字與 HTML 內文、附件(名稱、類型、大小、SHA-256)、HTML 連結。 - 共 5,967 次欄位比對:完全一致 5,902(嚴格一致率 98.9%);有差異但已記錄原因 65;無法解釋 0。
- 三方比對(本工具、Python、生成器寫入的已知答案):三方一致 602;本工具符合答案而 Python 不同 29;只有本工具有對應欄位(Received、驗證結果、指標)且符合 97。另有 4 次「與已知答案不同」的案例都已在已知差異清單中說明(例如 iso-2022-jp 編碼詞被切在跳脫序列中間,規格不允許)。
- mbox 分割:把語料合成 mbox,與 Python 的
mailbox模組比對,448 封全部切對、447 封位元組完全相同,另 1 封只差結尾換行(檔案最後一封沒有結尾換行)。 - 單元測試:共 1,387 項(編碼、標頭、MIME、HTML 與網址、路徑與驗證、指標、匯出、郵件庫、語料一致性),全數通過。
| 類別 | 郵件數 | 比對次數 | 完全一致 | 已記錄差異 | 無法解釋 |
|---|---|---|---|---|---|
| charset-body | 51 | 663 | 662 | 1 | 0 |
| charset-html | 12 | 180 | 180 | 0 | 0 |
| charset-header | 28 | 364 | 361 | 3 | 0 |
| rfc2047 | 18 | 234 | 234 | 0 | 0 |
| rfc2231 | 19 | 247 | 241 | 6 | 0 |
| structure | 26 | 362 | 360 | 2 | 0 |
| multipart | 11 | 145 | 143 | 2 | 0 |
| report | 3 | 39 | 38 | 1 | 0 |
| crypto | 5 | 65 | 64 | 1 | 0 |
| nested | 6 | 78 | 77 | 1 | 0 |
| inline | 3 | 45 | 45 | 0 | 0 |
| date | 51 | 663 | 654 | 9 | 0 |
| address | 39 | 507 | 498 | 9 | 0 |
| duplicate | 5 | 65 | 65 | 0 | 0 |
| minimal | 10 | 130 | 125 | 5 | 0 |
| broken | 30 | 392 | 374 | 18 | 0 |
| long-header | 8 | 104 | 104 | 0 | 0 |
| raw8bit | 5 | 65 | 61 | 4 | 0 |
| eol | 8 | 108 | 108 | 0 | 0 |
| magic | 31 | 403 | 403 | 0 | 0 |
| sizes | 17 | 221 | 221 | 0 | 0 |
| html | 30 | 450 | 450 | 0 | 0 |
| received | 15 | 195 | 195 | 0 | 0 |
| auth | 9 | 117 | 117 | 0 | 0 |
| realworld | 9 | 125 | 122 | 3 | 0 |
已知差異與原因
| 代號 | 次數 | 原因 |
|---|---|---|
addr-comment-name | 2 | 以括號註解寫顯示名稱(a@x (Name)):本工具把註解當成名稱;Python 名稱留空。 |
addr-domain-case | 1 | 網域大小寫:本工具統一轉小寫;Python 保留原樣。 |
addr-nonaddress | 1 | 地址欄位內容根本不是地址(this is not an address):本工具當作「只有顯示名稱、沒有地址」並註記;Python 把整段當成沒有網域的本機部分。 |
addr-semicolon | 1 | 用分號分隔地址(Outlook 舊式寫法):本工具容忍並註記;Python 只認逗號。 |
alt-nonbody-part | 1 | multipart/alternative 裡 text/plain、text/html 以外的版本(例如 text/calendar 邀請):本工具列為附件讓你看得到;Python 的 iter_attachments 對 alternative 一律不回傳。 |
b64-afterpad | 2 | base64 在填充(=)之後又接了資料(把兩段 base64 直接串起來的郵件):本工具繼續解碼並註記;Python 的結果是亂碼。 |
charset-guess | 3 | 沒有字元集宣告、宣告成 us-ascii 卻含 8-bit,或宣告的字元集認不得:本工具依 UTF-8、Big5、GBK、windows-1252 的順序猜並註記;Python 以 ascii 解碼,非 ASCII 位元組變成 U+FFFD。 |
crypto-control-part | 1 | PGP/MIME 的控制 part(application/pgp-encrypted,內容只有 Version: 1):本工具歸為加密資訊;Python 列為附件。 |
date-ampm | 1 | 含 AM/PM 的十二小時制:本工具容忍;Python 視為無效或忽略。 |
date-colon-tz | 1 | 時區寫成 +08:00(含冒號)不在 RFC 5322 內:本工具容忍;Python 視為無效或不同。 |
date-comment-mid | 1 | 日期中間夾註解(Mon, 06 Jan 2025 (comment) 10:00:00 +0800):RFC 5322 允許,本工具解析;Python 視為無效。 |
date-fractional | 1 | 秒數帶小數(10:00:00.123):本工具容忍並捨去小數;Python 視為無效。 |
date-gmtplus | 1 | “GMT+0800” 這種寫法不在 RFC 5322 內:本工具容忍並採用 +0800;Python 視為無效。 |
date-iso | 1 | ISO 8601 格式的日期(2025-01-06T10:00:00+08:00)出現在 Date 標頭:不合 RFC 5322,本工具容忍並註記;Python 視為無效。 |
date-leap | 1 | 閏秒(23:59:60):本工具以 23:59:59 處理並註記;Python 視為無效。 |
empty-first-part | 3 | 前言區出現一行與 boundary 相同的文字:依 RFC 2046 它就是首個分隔行,產生一個空的 part;Python 忽略空 part。 |
ew-in-param | 2 | 檔名參數內含 RFC 2047 encoded-word(Outlook 等系統常見,不合 RFC 2231):本工具解碼;Python 保留原字面。 |
ew-split-multibyte | 3 | 多位元組字元被切在兩個 RFC 2047 encoded-word 之間(舊系統常見,不合 RFC 2047 §5):本工具先把相鄰、同字元集的字詞位元組合併再解碼;Python 逐字詞各自解碼,切斷處產生 U+FFFD。 |
extra-text-part | 4 | 第二個以後、沒有檔名也不是 attachment 的純文字 part(郵件群組頁尾、Apple Mail 的圖文交錯):本工具視為內文的延伸,顯示時接在內文後面;Python 的 iter_attachments 把它列為附件。 |
mp-fallback-text | 4 | multipart 的 boundary 缺漏或對不上:本工具把整段內容當純文字顯示並註記;Python 保留 multipart 型別但沒有內文。 |
mp-noclose-eol | 2 | 缺少結尾邊界(郵件被截斷)時,最後一個 part 結尾的換行:本工具保留(沒有分隔行可以把它算進去);Python 去掉。 |
nested-cte | 1 | message/rfc822 帶了 base64 傳輸編碼(規格不允許,但寄信軟體偶有):本工具先解碼再解析內嵌郵件;Python 直接把 base64 文字當標頭解析。 |
py-error:LookupError | 2 | Python 在該欄位丟出例外(較嚴格) |
qp-blank-after-eq | 2 | quoted-printable 的軟換行 “=” 後面還有空白:RFC 2045 §6.7(3) 要求解碼時刪除行尾空白,因此仍是軟換行,本工具接合兩行;Python 原樣保留 “= ”。 |
raw-8bit-filename | 2 | 檔名參數內含未編碼的 8-bit 位元組(例如 Big5):本工具猜測字元集;Python 以 U+FFFD 取代。 |
raw-8bit-header | 4 | 標頭裡有未經 RFC 2047 編碼的 8-bit 位元組:本工具依 UTF-8、Big5、GBK、windows-1252 的順序猜測並加註記;Python 以 surrogateescape 讀入後顯示為 U+FFFD。 |
raw-8bit-name | 3 | 地址顯示名稱含未編碼的 8-bit 位元組:本工具猜測字元集並加註記;Python 顯示為 U+FFFD。 |
report-part | 1 | text/rfc822-headers(退信附的原信標頭):本工具歸為「退信報告」,不列入附件;Python 列為附件。 |
rfc2231-both-forms | 2 | 同一個參數同時有 filename 與 filename*(RFC 2231 延伸寫法):依 RFC 6266 §4.3 的建議,本工具以 filename* 為準;Python 取 filename。 |
single-part-attachment | 1 | 整封信只有一個非文字的 part(沒有 multipart):本工具把它列為附件;Python 的 iter_attachments 對非 multipart 郵件不回傳。 |
space-before-colon | 5 | 標頭名稱與冒號之間有空白(Subject : x):本工具接受;Python 視為標頭結束、其餘當內文。 |
subject-trim | 2 | 主旨前後的空白:本工具修剪(顯示與比對用);Python 保留編碼字詞內的尾端空白。 |
to-repeated | 1 | 同一封信有兩個 To 標頭:本工具把兩者合併顯示;Python 只取最前面那個。 |
year2-pivot | 1 | 兩位數年份的換算:RFC 5322 §4.3 以 50 為界(50–99 → 19xx,00–49 → 20xx);Python 的 email.utils 以 68 為界。 |
year3 | 1 | 三位數年份(例如 125,舊系統以 1900 起算的年數):RFC 5322 §4.3 規定加 1900,本工具依此換算;Python 照字面解讀為西元 125 年。 |
不支援或只支援一部分的格式
| 格式 | 狀態 | 頁面上的說明(摘要) |
|---|---|---|
| Outlook .msg | 不支援 | 本版不解析 OLE 複合檔案。替代做法(依 Outlook 版本而異):開啟該封郵件,從「檔案 → 內容」複製「網際網路標頭」貼到本頁;或把郵件拖曳到桌面另存成 .eml 再選取。頁面遇到 .msg 會顯示這段說明,不會假裝解析成功。 |
| 壓縮檔、Office 檔(zip) | 不支援 | 請先解壓縮,再選取裡面的 .eml 或 .mbox。 |
| TNEF(winmail.dat) | 部分支援 | 郵件本身照常解析;winmail.dat 附件只當成一般附件列出(含雜湊與下載),不展開裡面真正的附件,並在細節頁顯示說明。 |
| S/MIME(簽署或加密) | 部分支援 | 標頭、路徑與外層結構照常顯示;簽署封裝(signed-data)與加密封裝的內容不展開、不解密;multipart/signed 的內容照常顯示但不驗證簽章。畫面會寫明「無法解密」或「不驗證」。 |
| PGP(PGP/MIME 或內文區塊) | 部分支援 | 標頭與路徑照常;加密內容無法解密,只會看到加密區塊或密文附件,簽章不驗證。畫面會寫明。 |
| UTF-16 編碼的郵件文字檔 | 部分支援 | 64 MiB 以內自動轉成 UTF-8 後解析(整檔 SHA-256 仍是原始檔的);更大的請先另存成 UTF-8。 |
| 超大單封郵件 | 部分支援 | 單封超過 128 MiB 時只讀標頭與結構,不解析內文與附件,畫面會註明。 |
| 含 NUL、非郵件的二進位檔、空檔 | 不支援 | 顯示「看不出這是郵件檔」或「檔案是空的」,不會當成郵件硬解析。 |
如何自行驗證
- 用作業系統的 sha256sum(或 shasum -a 256、certutil -hashfile)對原檔計算,與來源檔表中的 SHA-256 比對。
- 用 Python 重算附件雜湊,與附件分頁或「全部附件雜湊」CSV 比對。下面的範例只用標準函式庫:
import email, hashlib
from email import policy
m = email.message_from_binary_file(open("sample.eml", "rb"), policy=policy.default)
for part in m.walk():
if part.get_filename():
data = part.get_payload(decode=True)
print(part.get_filename(), len(data), hashlib.sha256(data).hexdigest())- mbox 檔可用
mailbox.mbox("export.mbox")逐封讀出,與清單的封數與主旨比對;注意 Python 只還原恰好一層「>From 」,本工具預設還原任意長度的 >>…From 的一層,兩者在極少數情況會不同。 - 下載單封 .eml,與原始郵件檔(.eml)比較位元組;mbox 內的郵件是依 mboxrd 慣例還原後的位元組,與檔案裡的略有不同,報告與畫面都會註明。
- 驗證 DKIM 或 SPF 需要另外的工具與網路查詢(例如各郵件服務自己的驗證工具),本工具不提供。
常見誤解
- 「驗證結果寫 pass,就是真的。」那是信裡寫的文字,本工具沒有驗證,標頭也可能被手動編輯。
- 「驗證結果寫 fail,就是假的。」轉寄、郵件群組、寄信平台常造成失敗;失敗不等於偽造。
- 「沒有任何特徵提示,就可以放心。」特徵提示只是一小部分容易檢查的事實;沒有提示不代表可信。
- 「Received 最下面一筆的 IP 就是寄件人的位置。」只有你信任的收信伺服器之後的紀錄可靠,而且 IP 的所在地常與寄件人無關。
- 「Date 就是寄出的時間。」那是寄件者軟體填的,時鐘或時區錯誤、排程寄出都會讓它與實際時間不同。
- 「附件的檔名副檔名就是它的真實類型。」工具會另外依檔案開頭的位元組判斷內容,兩者不符時會提示,但判斷也可能不完整。
- 「顯示的網址就是點下去會到的地方。」HTML 郵件的顯示文字與實際連結可以不同;本頁列出實際連結並標示不一致,但不會替你判斷哪個安全。
建議的使用流程
- 保留原始郵件檔,不要修改;先用證據鏈帳本為原檔計算 SHA-256 並記錄加入時間。
- 在本頁解析,先看摘要與特徵提示,再逐封細看;把你關心的郵件的單封報告與附件雜湊匯出。
- 有疑問的附件,不要在日常電腦直接開啟;需要查看時請使用隔離環境。
- 需要驗證簽章、追查寄件來源或作為正式證據時,請由專業人員與律師處理。
程式與資料處理
- 解析器完全是本站自己寫的程式:郵件標頭與 MIME 解析、RFC 2047/2231 解碼、base64、quoted-printable、uuencode、Punycode、RFC 5322 日期、Received 與驗證結果標頭解析、HTML 掃描(不使用 DOM、不渲染)、網址 defang。沒有使用任何第三方函式庫,也沒有從網路載入任何程式。
- SHA-256 是自行實作的增量版本(大檔與整檔雜湊分塊讀取),測試時與 Python 的 hashlib 比對相同;字元編碼的解碼使用瀏覽器內建的 TextDecoder,對 windows-1252 另外用自己的對照表,避免瀏覽器之間的差異。
- 索引、搜尋、匯出都在同源的背景 Worker 內;整個頁面沒有 iframe、不使用 eval、不使用 Blob Worker,不載入任何遠端資源。畫面上顯示的郵件內容一律以純文字放入頁面。
- 不使用 localStorage、sessionStorage、IndexedDB 或 cookie;關閉或重新整理頁面就會清除畫面上的全部內容。