9.3 密碼學工程陷阱與最佳實務#
概念說明#
回顧全書的破密案例,你會發現一個規律:演算法本身極少被「正當破解」,被攻破的幾乎都是用法。WEP 死於 IV 重用、Enigma 死於程序錯誤、padding oracle 死於錯誤訊息、Flame 死於 MD5、Android 錢包死於 ECDSA 隨機數。密碼學工程的第一課:
不要自己設計協定、不要自己發明原語的用法;用標準、用高階函式庫、用預設值。
十大陷阱與對策#
| # | 陷阱 | 真實後果 | 對策 |
|---|---|---|---|
| 1 | 自製加密(把 Base64/自創 XOR 當加密) | 幾分鐘被破 | 用 AEAD 庫(4.2、4.3) |
| 2 | ECB 模式、固定 IV | 圖案洩漏、首區塊可操弄 | 只用 GCM/ChaCha20-Poly1305 |
| 3 | nonce/IV 重用 | CTR/GCM 全毀 | 計數器式 nonce、4.2 節紀律 |
| 4 | 用 == 比 MAC | 時序攻擊 | hmac.compare_digest |
| 5 | sha256(password) 存密碼 | GPU 字典秒破 | Argon2id/bcrypt/scrypt(慢、加鹽) |
| 6 | 長度延伸(H(secret||msg) 當 MAC) | Web 遠程利用 | HMAC(5.3、6.1) |
| 7 | 無完整性(只加密不驗證) | 竄改/填充側頻道 | AEAD 一律開 |
| 8 | 弱隨機數(random 模組當金鑰源) | 金鑰可預測 | secrets/os.urandom |
| 9 | 過期演算法(MD5/SHA-1/DES/RC4/RSA-1024) | 已知破法 | 現行推薦清單 |
| 10 | 憑證驗證被關閉(verify=False) | 中間人暢行 | 永不關閉;測試也不關 |
最佳實務清單#
# ✅ 隨機數:只用 secrets / os.urandom
import secrets, os
key = secrets.token_bytes(32) # 絕不用 random.randrange 當金鑰
# ✅ 密碼儲存:慢雜湊 + 鹽(argon2-cffi 或 hashlib 的 scrypt)
import hashlib
salt = secrets.token_bytes(16)
dk = hashlib.scrypt(b"password", salt=salt, n=2**15, r=8, p=1, dklen=32)
# 驗證時用同一參數重算 + compare_digest
# ✅ 對稱加密:AEAD,一次搞定機密性+完整性
from Crypto.Cipher import AES
nonce = os.urandom(12)
c = AES.new(key, AES.MODE_GCM, nonce=nonce)
ct, tag = c.encrypt_and_digest(b"data")
# ✅ 金鑰派生:HKDF,不要直接用共享祕密當加密金鑰
import hmac as _h
def hkdf_ikm_to_key(ikm: bytes, info: bytes, salt: bytes) -> bytes:
prk = _h.new(salt, ikm, hashlib.sha256).digest()
return _h.new(prk, info + b"\x01", hashlib.sha256).digest()
# ✅ 常數時間比較(MAC、token、密碼雜湊比對一律如此)
ok = hmac.compare_digest(tag, received_tag)
其他工程要點:
- 錯誤訊息統一化:解密失敗、驗證失敗、格式錯誤都回同一句話(padding oracle 的教訓,4.2 節)。
- 金鑰輪替與前向保密:長期金鑰只做「驗證」,資料加密用暫態金鑰(TLS 1.3 的做法)。
- 密碼敏捷性(crypto-agility):把演算法與參數設為可替換的欄位,演算法退役(如 SHA-1)時不必重寫系統。
- 最小資訊原則:不加密的資料(日誌、快取、URL)往往才是洩漏點。
- 側頻道意識:處理金鑰的程式碼要常數時間(避免依金鑰內容分支、避免查表洩漏快取)——自寫大數運算是高危行為。
安全性分析#
本節的後設結論:密碼學工程的安全 = 演算法(標準)× 模式(AEAD)× 參數(長度)× 實作(常數時間、驗證)× 金鑰管理(生成、輪替、儲存)× 協定(防重放、防降級)。任何一項歸零,整體歸零。這也是為什麼 9.1 節的 TLS 1.3 選擇「大幅刪減選項」——把做錯的機會從設計層面移除,是比「提供更多選擇」更先進的安全工程。
練習#
- 程式碼審查題:給定一段使用
random.randint產生 AES 金鑰、ECB 模式、==比 MAC 的「登入系統」程式碼,找出至少 5 個問題並逐項改寫。 - 用
hashlib.scrypt與hashlib.sha256各對一萬個密碼做雜湊並計時,量化「慢雜湊」對字典攻擊的抑制效果。 - 為你的專案寫一份「密碼學使用檢查表」(十項內),之後每次 review 都照表檢查——這是本書希望你帶走的長期習慣。