Hash 雜湊演算法完整比較:MD5 / SHA1 / SHA256 / SHA512 該選哪個?

寫給:仍在用 MD5 存使用者密碼的後端工程師、不確定 git SHA-1 安不安全的 DevOps、想知道下載檔案 hash 驗證怎麼做的所有用戶。

Hash(雜湊)演算法是把任意大小資料壓縮成固定長度指紋的單向函數。它支撐了 Git 版本管理、HTTPS 證書、區塊鏈、檔案完整性檢查、密碼儲存等幾乎所有現代資安基礎。但 hash 演算法不是一視同仁——MD5 早在 2004 年被破、SHA1 在 2017 年被 Google + 阿姆斯特丹大學暴力破解,仍用這些演算法等於把家門鑰匙貼公告欄。本文 14 分鐘把所有主流 hash 演算法的速度、安全、用途一次講清楚。

1. Hash 是什麼

Hash 函數有四個必要性質:

  1. 確定性:同樣輸入永遠產生同樣輸出
  2. 快速計算:常數時間
  3. 不可逆:從 hash 推不回原文(單向函數)
  4. 碰撞抗性:找到兩個不同輸入產生同樣 hash 極困難

舉例 SHA-256:

echo -n "hello" | shasum -a 256
# 2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824

不論輸入 1 byte 或 1GB,輸出永遠 256 bit(64 個 hex 字元)。即使輸入只差一個字,輸出完全不同(雪崩效應):

echo -n "hello" | shasum -a 256  # 2cf24dba5fb...
echo -n "hellp" | shasum -a 256  # b1a8d6c5d22...

Hash 的常見用途:

  • 檔案完整性驗證(下載 ISO 後比對)
  • Git commit ID(SHA-1 / SHA-256)
  • 密碼儲存(不存明文)
  • 區塊鏈共識(PoW)
  • 內容定址(IPFS)
  • Bloom filter、HashMap 索引

2. 5 個核心觀念

2.1 演算法總覽

演算法 輸出長度 設計年份 狀態 用途
MD5 128 bit 1992 已破 非安全場景(checksum)
SHA-1 160 bit 1995 已破 git(漸進淘汰)
SHA-256 256 bit 2001 安全 大部分場景
SHA-512 512 bit 2001 安全 高安全需求
SHA-3 (Keccak) 224/256/384/512 bit 2015 安全 不同設計基礎
BLAKE2 256/512 bit 2012 安全 比 SHA-256 快 3 倍
BLAKE3 256 bit(可變) 2020 安全 比 BLAKE2 快 10 倍
xxHash 32/64/128 bit 2012 非加密 純為速度(HashMap)

選擇原則(2026 年):

  • 一般 hash:SHA-256 或 BLAKE3
  • 密碼存儲:argon2id 或 bcrypt(不是 hash 函數!)
  • 非加密快速 hash:xxHash / FarmHash

2.2 為什麼 MD5 / SHA-1 不能用

碰撞攻擊歷史:

  • MD5:2004 年 Xiaoyun Wang 等中國研究團隊找到實際碰撞,2012 年 Flame 惡意程式偽造 Microsoft 證書
  • SHA-1:2017 年 Google SHAttered 計畫公開找到兩個不同 PDF 有相同 SHA-1,成本約 11 萬美金(2026 年估算降到約 5 萬)

對攻擊者意義:可以偽造合法檔案。下載者比對 hash 看似一致,實際內容已替換。

只有一個場景仍可用 MD5/SHA-1:純粹做 cache key / 索引,不涉及安全信任。

2.3 SHA-256 vs SHA-512

兩者來自同一家族(SHA-2),主要差別:

屬性 SHA-256 SHA-512
輸出長度 32 byte 64 byte
內部 word 32 bit 64 bit
在 64-bit CPU 速度 慢 快 30%
在 32-bit / IoT 設備 快 慢
安全強度 128 bit 256 bit

2026 年伺服器幾乎都 64-bit,SHA-512 更快。但 SHA-256 是事實標準(HTTPS 證書、區塊鏈),相容性最廣。

2.4 密碼雜湊:bcrypt / argon2,不是 SHA

這是新手最常犯的錯誤。用 SHA-256 存密碼等於用紙糊保險箱:

  • SHA-256 設計目標是快(每秒可算數十億次)
  • 攻擊者用 GPU 字典攻擊一秒試 100 億組密碼
  • 加 salt 也救不了,依然是秒級破解

正確做法用故意慢的密碼雜湊函數:

函數 年份 推薦設定
bcrypt 1999 cost factor 12
scrypt 2009 N=2^17, r=8, p=1
Argon2id 2015(PHC 競賽冠軍) m=64MB, t=3, p=4

argon2id 是 2026 年最推薦選擇。它同時抵抗 GPU 與 ASIC 攻擊。

import argon2
ph = argon2.PasswordHasher()
hash = ph.hash("user-password")
# $argon2id$v=19$m=65536,t=3,p=4$...

try:
    ph.verify(hash, "user-password")
    print("OK")
except argon2.exceptions.VerifyMismatchError:
    print("Wrong password")

2.5 Salt + Pepper

Salt:每個使用者一個隨機字串,防 rainbow table 攻擊。bcrypt / argon2 自動加 salt。

Pepper:所有使用者共用一個秘密字串,存在程式碼或 HSM 而非 DB。若 DB 被偷,攻擊者沒有 pepper 仍無法 brute force。

import argon2, hmac, hashlib

PEPPER = os.environ["APP_PEPPER"]  # 32+ byte 隨機字串

def hash_password(password):
    peppered = hmac.new(PEPPER.encode(), password.encode(), hashlib.sha256).hexdigest()
    return argon2.PasswordHasher().hash(peppered)

3. 實戰範例

範例 1:下載檔案後驗證 SHA-256

# 下載 Ubuntu ISO
curl -O https://releases.ubuntu.com/24.04/ubuntu-24.04-desktop-amd64.iso

# 算 sha256
shasum -a 256 ubuntu-24.04-desktop-amd64.iso
# 9bc...

# 比對官方公告
echo "9bc...example  ubuntu-24.04-desktop-amd64.iso" | shasum -a 256 -c
# ubuntu-24.04-desktop-amd64.iso: OK

範例 2:Python 對檔案做 hash

import hashlib

def file_hash(path, algo="sha256", chunk=8192):
    h = hashlib.new(algo)
    with open(path, "rb") as f:
        while data := f.read(chunk):
            h.update(data)
    return h.hexdigest()

print(file_hash("data.bin"))
print(file_hash("data.bin", "blake2b"))

chunk 控制記憶體用量,1GB 檔案不會 OOM。

範例 3:HMAC 簽章(防偽造)

import hmac, hashlib

key = b"my-secret-key"
message = b"transfer 1000 to alice"
sig = hmac.new(key, message, hashlib.sha256).hexdigest()
print(sig)

# 驗證
expected = hmac.new(key, message, hashlib.sha256).hexdigest()
if hmac.compare_digest(sig, expected):
    print("Valid")

HMAC 比直接 hash(key + message) 安全(避免長度延伸攻擊)。

範例 4:Argon2 密碼雜湊(生產用)

from argon2 import PasswordHasher
from argon2.exceptions import VerifyMismatchError, InvalidHashError

ph = PasswordHasher(memory_cost=65536, time_cost=3, parallelism=4)

# 註冊
hashed = ph.hash("user-input-password")
db.save_user(username, hashed)

# 登入
stored = db.get_user_hash(username)
try:
    ph.verify(stored, "user-input-password")
    if ph.check_needs_rehash(stored):
        new_hash = ph.hash("user-input-password")
        db.update_hash(username, new_hash)
    return True
except (VerifyMismatchError, InvalidHashError):
    return False

check_needs_rehash 是好習慣:當你升級 parameters 時,登入時自動重 hash。

範例 5:Git 加 SHA-256 取代 SHA-1

# 創建 SHA-256 倉庫(Git 2.29+)
git init --object-format=sha256 my-repo
cd my-repo
echo "hello" > file.txt
git add . && git commit -m "init"
git log --oneline
# 4d186321...  init  ← 64-char SHA-256,不再是 40-char SHA-1

GitHub 2026 已支援 SHA-256,但跨 server 推送仍需配對相同 format。

4. 進階技巧

4.1 Constant-time 比較防 timing attack

import hmac
hmac.compare_digest(received_sig, expected_sig)  # ✓
received_sig == expected_sig                      # ✗ 早 return

逐 byte 比較會在第一個不同字元 early return,攻擊者測時間差可一字一字猜中正確 sig。constant-time 比較永遠比完全長度。

4.2 BLAKE3 並行加速

import blake3
hasher = blake3.blake3()
with open("huge.bin", "rb") as f:
    while chunk := f.read(65536):
        hasher.update(chunk)
print(hasher.hexdigest())

# 並行
import blake3
blake3.blake3(b"data", max_threads=blake3.blake3.AUTO).hexdigest()

BLAKE3 在多核 CPU 可達 SHA-256 的 10 倍速。

4.3 增量 hash(resumable)

下載大檔案被中斷?hash 狀態可以 dump 出來下次續用:

import hashlib, pickle

h = hashlib.sha256()
h.update(b"first part")

# 保存中間狀態(Python 限定,需 hashlib >= 3.11)
state = h.copy()

# 繼續
state.update(b"second part")
print(state.hexdigest())

4.4 內容定址儲存(CAS)

IPFS、Git 都用 hash 當儲存 key。設計重點:

  • 同樣內容只存一份(自動 dedup)
  • 改任何一 byte 就變新 hash(不可變)
  • 樹狀結構(Merkle tree)可做局部驗證
def store(data):
    h = hashlib.sha256(data).hexdigest()
    path = f"objects/{h[:2]}/{h[2:]}"  # 分散到 256 子目錄
    if not os.path.exists(path):
        os.makedirs(os.path.dirname(path), exist_ok=True)
        with open(path, "wb") as f:
            f.write(data)
    return h

4.5 Bloom filter 結合 hash

判斷 element 是否在集合中、可容忍 false positive:

import mmh3  # MurmurHash3
class BloomFilter:
    def __init__(self, size=10000, k=3):
        self.bits = [0] * size
        self.size = size
        self.k = k

    def add(self, item):
        for i in range(self.k):
            idx = mmh3.hash(item, i) % self.size
            self.bits[idx] = 1

    def maybe_contains(self, item):
        return all(self.bits[mmh3.hash(item, i) % self.size] for i in range(self.k))

5. 常見錯誤與解法

錯誤 1:用 SHA-256 存密碼

症狀:DB 被偷後,攻擊者數小時內破解所有密碼。 根因:SHA-256 太快,配合 GPU 字典攻擊秒破。 解法:改用 argon2id(推薦)或 bcrypt。

錯誤 2:自己 salt 但結合方式錯

症狀:sha256(salt + password) 仍被破。 根因:長度延伸攻擊 + GPU 仍夠快。 解法:不要自己做密碼雜湊。使用標準 lib(argon2 / bcrypt)。

錯誤 3:用 hash 比較字串造成 timing attack

症狀:API key 驗證被時序攻擊推出正確值。 根因:用 == 比較。 解法:用 hmac.compare_digest。

錯誤 4:忘了 salt 隨機性

症狀:每個 user 用相同 salt(例如 user id)。 根因:開發圖方便。 解法:salt 必須是密碼學等級隨機(16 byte 以上)。bcrypt / argon2 自動產。

錯誤 5:MD5 用於檔案驗證 + 安全信任

症狀:依賴 MD5 驗證下載檔,被攻擊者偽造。 根因:MD5 已破。 解法:改用 SHA-256 並驗證簽章(GPG sign)。

6. 用 SuperTools 線上版完成相同任務

不想開 terminal?打開 SuperTools Hash 工具:

  • 支援演算法:MD5 / SHA1 / SHA256 / SHA384 / SHA512 / BLAKE2 / BLAKE3
  • 文字 / 檔案雙模式:拖檔上去即時算 hash
  • HMAC 模式:填 key 算 HMAC-SHA256 等
  • 批次比對:貼一個 expected hash,工具自動 highlight 一致與否
  • 編碼選擇:hex / base64 / base64url

工具 client-side WebCrypto + WASM 跑,檔案不上傳。可拿來算公司內部檔案 checksum。

實際使用流程:

  1. 從官網複製預期的 SHA-256
  2. 開 supertools.tw/dev/hash 拖入下載的檔案
  3. 選 SHA-256
  4. 比對工具計算值與貼上的預期值

立即使用 SuperTools Hash 工具:https://supertools.tw/dev/hash

7. 其他語言實作

Python

import hashlib
hashlib.sha256(b"hello").hexdigest()
hashlib.blake2b(b"hello").hexdigest()

JavaScript(Web Crypto)

async function sha256(text) {
  const buf = new TextEncoder().encode(text);
  const hash = await crypto.subtle.digest("SHA-256", buf);
  return Array.from(new Uint8Array(hash))
    .map(b => b.toString(16).padStart(2, "0")).join("");
}

Node.js

const crypto = require("crypto");
crypto.createHash("sha256").update("hello").digest("hex");

Go

import "crypto/sha256"
import "encoding/hex"
hex.EncodeToString(sha256.Sum256([]byte("hello"))[:])

Rust

use sha2::{Sha256, Digest};
let result = Sha256::digest(b"hello");
println!("{:x}", result);

Bash

echo -n "hello" | shasum -a 256
echo -n "hello" | sha256sum  # Linux
echo -n "hello" | openssl dgst -sha256

8. FAQ

Q1:MD5 完全不能用嗎? 不能用於安全場景(簽章、密碼、信任)。可用於純粹 cache key、HashMap、非安全 checksum。

Q2:SHA-256 永遠安全嗎? 量子電腦 Grover 演算法理論上可把 256 bit 安全強度降到 128 bit,仍足夠 2050 年前。

Q3:BLAKE3 為什麼還沒普及? 較新(2020),相容性還沒到 SHA-256 等級。新專案可考慮,跨平台需注意。

Q4:用 SHA-512 取代 SHA-256 有意義嗎? 64-bit 伺服器上 SHA-512 更快,但相容性較差。除非有 256 bit 安全強度需求,否則沿用 SHA-256。

Q5:bcrypt 跟 argon2 怎麼選? 新專案首選 argon2id;既有 bcrypt 系統可保留(cost factor 升到 12+)。

9. 實戰案例研究

案例 1:Flame 惡意程式偽造 Microsoft 證書

2012 年發現的 Flame 中東惡意程式利用 MD5 碰撞攻擊:

  1. 攻擊者找出兩個 MD5 相同的證書—— 一個合法 Microsoft Terminal Server license server 證書、一個假冒 Windows Update 簽章證書
  2. Microsoft 簽合法那個 → 攻擊者用假冒的偽造 Windows Update
  3. 數以萬計 Windows 自動下載「合法簽名」的惡意更新

事後 Microsoft 緊急在 2012 年 6 月發布 KB2718704 撤銷相關證書,並全面廢棄 MD5。

教訓:MD5 用在簽章 = 給攻擊者開後門。

案例 2:LinkedIn 2012 6.5 million 密碼洩漏

LinkedIn 2012 被攻擊洩出 6.5 million 帳號的 SHA-1 密碼 hash(無 salt)。攻擊者用 GPU 字典攻擊 6 天破解 90%。

更糟的是同樣 hash 在 2016 重新外洩,這次洩 1.65 億筆(4 年累積)。

修法(後續業界普遍):

  1. 立刻全 user 重設密碼
  2. 改用 bcrypt
  3. 密碼加 salt + pepper
  4. 強制 MFA

教訓:用快 hash + 無 salt 存密碼 = 等著被破。

案例 3:Git SHA-1 轉 SHA-256

2017 年 Google + 阿姆斯特丹大學 SHAttered 計畫公開 SHA-1 碰撞攻擊,成本約 11 萬美元。Git 用 SHA-1 當 commit ID 引起社群恐慌。

Git 團隊 2018-2021 漸進加入 SHA-256 支援:

  • 2.29(2020):--object-format=sha256 新增 repo
  • 2.42(2023):SHA-256 / SHA-1 倉庫互通工具
  • GitHub 2025 起新 repo 預設 SHA-256

移轉考量:

  • 跨 server push 需配對相同 format
  • CI/CD 取 commit hash 邏輯要更新
  • third-party tool(GitHub Actions / GitLab CI)要支援

教訓:底層 hash 演算法的升級需數年漸進,不能等到全壞才動。

10. 結論 + 延伸閱讀

Hash 演算法的選擇不是「越強越好」,而是「對應場景」。一般 hash 用 SHA-256 或 BLAKE3、密碼存儲一律用 argon2id、HMAC 簽章用 SHA-256 + constant-time 比較。下次面對 hash 需求時,先問三件事:要不要碰撞抗性?要不要刻意慢?要不要可平行?答案出來演算法自然就定了。三條黃金法則:安全相關別碰 MD5/SHA-1、密碼用 argon2id 不要用 hash、比較簽章用 constant-time。

延伸閱讀: