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 位元輸出)。
應用場景#
- 完整性驗證:下載檔案附 SHA-256 校驗值(注意:這只能防意外損壞;防惡意竄改需要簽章或可信管道,因為攻擊者可同時換掉檔案與校驗值)。
- 密碼儲存:存
Argon2/bcrypt/scrypt/PBKDF2(password, salt)而非密碼本身。不可用普通雜湊直接存——攻擊者拿到資料庫後可高速字典/彩虹表攻擊;密碼雜湊必須「慢 + 加鹽 + 有記憶體成本」。 - 資料結構:Git 的內容定址、Merkle 樹(9.2 節區塊鏈)、去重儲存。
- 數位簽章與 MAC 的地基:先雜湊再簽(6.2 節)、HMAC(6.1 節)。
- 承諾(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 節)。
練習#
- 計算「找 4 個十六進位零開頭的雜湊」平均需多少次嘗試?把程式改成 6 個零,感受工作量的指數成長。
- 用生日攻擊的數學估計:SHA-256 要多少次雜湊運算才可能找到碰撞?若一部電腦每秒算 10^9 次,需要多少年?
- 解釋:為什麼「檔案下載頁附 MD5 校驗值」對防禦意外損壞有效,對防禦駭客竄改安裝檔幾乎無效?應該改用什麼?