跳轉到

5.1 雜湊的性質與應用#

概念說明#

密碼學雜湊函數把任意長度的輸入壓縮成固定長度的輸出(SHA-256 為 256 位元),記作 h = H(M)。它有三個關鍵外觀特徵:單向(給 h 難回推 M)、確定性(同輸入必同輸出)、雪崩效應(輸入改 1 bit,輸出約半數 bit 翻轉)。

三大安全性性質#

性質 定義 對應的攻擊
前像抗性(preimage resistance) 給定 h,難找到 M 使 H(M)=h 由雜湊值還原原文
第二前像抗性(second preimage resistance) 給定 M,難找到 M'≠M 使 H(M')=H(M) 偽造特定文件
抗碰撞(collision resistance) 難找到任意 M≠M' 使 H(M')=H(M) 兩份文件同雜湊(生日攻擊見 5.2)

三者強度不同:n 位元輸出的雜湊,前像/第二前像攻擊約需 2^n 次運算,碰撞攻擊只需約 2^(n/2) 次(生日悖論)——因此設計時必須讓 n/2 也夠大(128 位元安全 ⇒ 至少 256 位元輸出)。

應用場景#

  1. 完整性驗證:下載檔案附 SHA-256 校驗值(注意:這只能防意外損壞;防惡意竄改需要簽章或可信管道,因為攻擊者可同時換掉檔案與校驗值)。
  2. 密碼儲存:存 Argon2/bcrypt/scrypt/PBKDF2(password, salt) 而非密碼本身。不可用普通雜湊直接存——攻擊者拿到資料庫後可高速字典/彩虹表攻擊;密碼雜湊必須「慢 + 加鹽 + 有記憶體成本」。
  3. 資料結構:Git 的內容定址、Merkle 樹(9.2 節區塊鏈)、去重儲存。
  4. 數位簽章與 MAC 的地基:先雜湊再簽(6.2 節)、HMAC(6.1 節)。
  5. 承諾(commitment):先公布 H(secret),之後揭曉 secret 驗證——線上競猜先「鎖答案」。

Python 實作#

import hashlib

data = "密碼學 hash".encode()
print(hashlib.sha256(data).hexdigest())

# 雪崩效應
a = hashlib.sha256(b"attack at dawn").hexdigest()
b = hashlib.sha256(b"attack at daxn").hexdigest()
print(a[:16], b[:16])   # 完全不同

# 簡易完整性檢查(防意外損壞)
def file_sha256(path):
    h = hashlib.sha256()
    with open(path, "rb") as f:
        for chunk in iter(lambda: f.read(65536), b""):
            h.update(chunk)
    return h.hexdigest()

# 示範前像之難:找 "0000" 開頭的部分前像(工作量證明的原型)
prefix, i = hashlib.sha256(b"x").hexdigest(), 0
while not hashlib.sha256(str(i).encode()).hexdigest().startswith("0000"):
    i += 1
print("找到 nonce:", i, "平均需約 65536 次嘗試")  # 16^4

安全性分析#

  • 被淘汰者:MD5(128 位元,2004 年實際碰撞,1996 年起即有理論弱點)、SHA-1(160 位元,2017 年 SHAttered 攻擊實際碰撞)——任何新程式禁止使用。
  • 現行推薦:SHA-256/SHA-512(SHA-2 家族)、SHA3-256(Keccak,結構不同,見 5.2)、BLAKE2/BLAKE3。
  • 常見誤用:(a) 用 MD5 存密碼;(b) 用「雜湊當簽章」——雜湊無金鑰,任何人都能重算,不具驗證身分功能;(c) 拿 H(secret || message) 當 MAC——落入長度延伸攻擊(5.3 節)。

練習#

  1. 計算「找 4 個十六進位零開頭的雜湊」平均需多少次嘗試?把程式改成 6 個零,感受工作量的指數成長。
  2. 用生日攻擊的數學估計:SHA-256 要多少次雜湊運算才可能找到碰撞?若一部電腦每秒算 10^9 次,需要多少年?
  3. 解釋:為什麼「檔案下載頁附 MD5 校驗值」對防禦意外損壞有效,對防禦駭客竄改安裝檔幾乎無效?應該改用什麼?