去年冬天我給 NAS 做了一次冷備份,兩顆 4TB 硬碟,rsync 跑了將近十一小時,跑完只看了結束碼是 0 就收工。兩個月後要還原一個記帳用的壓縮檔,解壓時報 unexpected end of archive——那個包本來 2.1GB,磁碟裡只剩 1.4GB,中間某次寫入悄悄斷了。我沒有任何辦法證明其他三百多個包是好的。從那之後我的備份流程裡多了一步:算完雜湊、把清單存到另一台機器上。雜湊到底能證明什麼、對哪一類問題無能為力,是那之後才弄清楚的。
一、雜湊是內容的指紋,不是加密
MD5 把任意長度的資料壓成 128 位元、通常寫成 32 個十六進位字元的一串。三個特性決定了它能做什麼:輸出定長,一個 3 位元組的文字和一個 4GB 映像檔算出來的結果一樣長;單向不可逆,拿到 5d41402abc4b2a76b9719d911017c592 也推不回原文(它是 hello 的 MD5,空字串的是 d41d8cd98f00b204e9800998ecf8427e);雪崩效應,改一個位元,結果面目全非。
所以「MD5 加密」這個說法本身是錯的,它只能回答兩個問題:這兩份資料是不是同一份?這份資料還是不是我拿到的那一份?想保密要靠加密,想存口令要靠加鹽的慢雜湊,兩者都不該拿 MD5 頂替。
二、校驗的關鍵其實是「期望值從哪來」
很多人把流程記成「下載—算雜湊—比對」,但決定結論價值的只有第三步裡參照物的來源。分開看:
| 期望值來源 | 校驗通過說明什麼 | 校驗失敗說明什麼 |
|---|---|---|
| 官網 HTTPS 頁面單獨公布 | 檔案與官網那份一致,沒有在傳輸中壞掉 | 傳輸截斷、被替換或版本拿錯,重新下載 |
| 和檔案同一個第三方下載站 | 只能說明這兩樣東西配套,幾乎不能說明安全 | 連站家的說明都抄錯了 |
| GPG/程式碼簽章,另附公開金鑰 | 來源可信且未被竄改,這才是防竄改 | 簽章無效,別安裝 |
| 自己上次備份時記錄的清單 | 這段時間裡檔案沒變、沒壞 | 儲存媒體或寫入出問題了,立刻找另一份副本 |
中間那一行是我覺得最該寫進說明書的:攻擊者替換安裝包時,順手改掉頁面上的 MD5 只需要幾秒。雜湊保證的是「完整性」,不是「真實性」,後者要靠簽章和管道信任。
三、我實際怎麼算,用哪些指令
系統內建的指令先記住,不依賴網路也能用:
| 平台 | 指令 | 輸出 |
|---|---|---|
| Windows(cmd) | certutil -hashfile app.iso MD5 | 32 位元十六進位 |
| Windows(PowerShell) | Get-FileHash -Algorithm SHA256 app.iso | 64 位元十六進位 |
| macOS | md5 app.iso / shasum -a 256 app.iso | MD5 / SHA-256 |
| Linux、Git Bash | sha256sum app.iso;整批 sha256sum -c SHA256SUMS | 逐行 OK / FAILED |
手邊只有一兩個檔案、不想敲路徑時,我把它們直接拖進檔案 MD5 計算:批次拖入後一次得到 MD5、SHA-1、SHA-256 三種值,同時列出檔名、大小、類型與修改時間,雜湊相同的會標紅——上次清重複安裝包就是靠這個標紅挑出來的。計算用瀏覽器內建的介面完成,檔案不上傳;幾十 GB 的映像檔我還是用命令列更穩。
備份這邊我的固定動作是三步:拷完 → 在來源端產生一份 SHA256SUMS.txt → 把清單複製到另一台機器。還原前 -c 跑一遍,兩百多個包哪幾個壞了直接看得到。文字類的短字串(介面簽章用的原文)我另開一頁算,MD5 工具支援 16 位與 32 位、大小寫切換和多行批次。
四、MD5 現在還能不能信
答案取決於對手是誰。MD5 的碰撞已經被實際構造出來,2008 年研究者利用它偽造過 CA 憑證,所以對抗性場合都不該再用:軟體簽章、憑證鏈、口令儲存,一律換 SHA-256 起步。反過來,「我這份檔案在拷貝時有沒有斷」這種非對抗場景它照樣夠用,因為壞檔不是有人專門針對你的檔案構造出來的。
| 用途 | 合適的演算法 | 原因 |
|---|---|---|
| 下載完整性、備份自檢 | MD5 或 SHA-256 | 只需發現無意損壞 |
| 安裝包、映像檔對外公布 | SHA-256 + 簽章 | MD5 可被碰撞,擋不住蓄意替換 |
| 口令儲存 | bcrypt / scrypt / Argon2 | MD5 太快,加鹽也救不了 |
| 介面簽章、防重放 | HMAC-SHA256 | 需要帶金鑰的認證碼,雜湊計算器一次出 MD5 到 SHA-512 六種結果並支援 HMAC |
| 檔案去重 | MD5 起步 | 速度快;重要資料去重後再抽查一次內容 |
五、幾個會讓人白排查的坑
- 16 位和 32 位不是兩種演算法:16 位只是取 32 位結果的第 9 到 24 個字元。比對前先確認長度,不然會以為「完全對不上」。
- 大小寫:
a-f與A-F是同一個值,只是顯示格式。腳本比對前統一轉小寫。 - 換行符號改變文字雜湊:同一份
README.md在 Windows 下是 CRLF、Linux 下是 LF,MD5 必然不同;Git 的core.autocrlf也會造成同樣結果。比文字雜湊前先統一行尾。 - 改名、改時間不影響雜湊:指紋只算內容,所以「我改了檔名雜湊應該變」的直覺是錯的,也正因如此它才適合用來判斷兩份內容是否相同。
- 外層包和內層檔案是兩回事:zip 的雜湊一致只保證包沒壞,包內有沒有被替換要看逐檔清單。
- 目錄沒有單一雜湊:檔案順序、權限、時間戳都會改變打包結果。核對整個目錄要用逐檔清單或 rsync 的校驗模式。
六、常見疑問
Q:兩個檔案 MD5 相同就一定完全一樣嗎?
日常場景可以這麼判斷,隨機損壞導致相同的機率低到可以忽略。只有當你面對一個會主動構造碰撞的對手時才需要升級演算法。
Q:官網只給 SHA-256,我機器上只有 md5sum 怎麼辦?
換演算法而不是換檔案:PowerShell 的 Get-FileHash、macOS 的 shasum -a 256、Linux 的 sha256sum 都能直接算,不必重新下載。
Q:校驗不一致該怎麼處理?
先重新下一次再算,仍不一致就當它被動過:不要執行,不要用其中的檔案,同時確認自己是不是拿錯了版本或是從鏡像站下載的。
Q:備份一定要留雜湊清單嗎,直接定期重拷不行?
重拷不能告訴你磁碟裡那份是不是已經壞了,只能賭。清單的成本是幾十秒計算,收益是還原前就知道哪些包可用。
Q:算大檔案要多久?
主要卡在磁碟讀取而不是演算法,同一顆硬碟上 SHA-256 和 MD5 的耗時差別往往小於讀寫速度本身。機械硬碟上等幾分鐘正常,瀏覽器裡過大的檔案還會受記憶體限制。
那次壞檔最後沒有造成實質損失——同批資料在另一台機器上還有一份舊副本,只缺了兩個月。真正改變的是流程:現在備份一結束我就把雜湊清單寫到第二份儲存上,因為「有一份備份」和「知道備份是好的」是兩回事。