單純的雜湊運算已無法有效防止密碼被破解,透過以下技巧可以有效抵禦彩虹表攻擊、大規模離線暴力破解,以及部分資料外洩所帶來的風險。
- 鹽值(Salt): 加入隨機性
- 迭代(Iteration):增加破解運算成本
- Pepper:使用額外的秘密值
Salt
- 定義:鹽值是一個隨機生成的字串,在雜湊前與輸入(通常是密碼)結合,改變雜湊的輸出結果。每次雜湊時,鹽值通常不同,且會與雜湊值一起儲存。
- 作用:
- 確保即使相同的輸入(密碼)也產生不同的雜湊值,防止某些攻擊方式。
- 鹽值本身不需保密,通常與雜湊值一起儲存在資料庫中。
- 例子:
- 密碼:”myPassword”
- 鹽值:X7y9z0w2k4m6n8p0(隨機生成)
- 結合後:”myPassword+X7y9z0w2k4m6n8p0″
- 計算雜湊:SHA-256(“myPassword+X7y9z0w2k4m6n8p0”) = abc123…
- 儲存:[鹽值: X7y9z0w2k4m6n8p0, 雜湊值: abc123…]
不加鹽值的風險
- 問題 1:相同密碼產生相同雜湊:
- 一般雜湊函數(如 SHA-256)是確定性的,相同輸入總是產生相同輸出。
- 例如:所有使用 “myPassword” 的用戶,他們的 SHA-256 雜湊值都是 34819d7bee…。
- 如果駭客竊取資料庫,看到相同的雜湊值,就知道這些用戶密碼相同。
- 問題 2:彩虹表攻擊(Rainbow Table Attack):
- 駭客可能使用彩虹表(預計算的雜湊值對應表),將常見密碼(如 “password123″)和其雜湊值儲存。
- 竊取資料庫後,駭客比對雜湊值即可反查密碼。
- 例如:如果資料庫儲存 34819d7bee…,駭客查彩虹表,立即知道對應密碼是 “myPassword”。
- 問題 3:暴力破解簡單:
- 常見密碼(如 “123456”)的雜湊值可能已被預計算,駭客可快速破解。
加鹽值如何提高安全性
- 防止相同密碼產生相同雜湊:
- 每個用戶的密碼都與一個獨特的隨機鹽值結合,即使密碼相同,雜湊值也不同。
- 例如:
- 用戶 A:密碼 “myPassword”,鹽值 X7y9z0w2,雜湊 abc123…
- 用戶 B:密碼 “myPassword”,鹽值 k4m6n8p0,雜湊 def456…
- 駭客無法從相同雜湊值推測密碼相同。
- 阻斷彩虹表攻擊:
- 彩虹表是針對純密碼的預計算雜湊表,加入隨機鹽值後,駭客必須為每個鹽值重新計算彩虹表,這幾乎不可能(因為鹽值數量龐大)。
- 例如:駭客的彩虹表可能包含 “myPassword” -> 34819d7bee…,但不會包含 “myPassword+X7y9z0w2” -> abc123…。
- 增加破解難度:
- 駭客必須針對每個用戶的鹽值單獨進行暴力破解(逐一嘗試所有可能密碼),而不是用通用的彩虹表。
- 結合 Bcrypt 等慢速雜湊函數(有工作因子),破解一個密碼可能需要數秒,破解整個資料庫則需要數年。
比較加鹽和不加鹽
假設一個網站儲存用戶密碼,比較不加鹽值和加鹽值的效果。
不加鹽值(不安全)
- 用戶 A 和 B 都用密碼 “myPassword”。
- 網站用 SHA-256 計算雜湊:
user A:SHA-256("myPassword") = 34819d7bee...
user B:SHA-256("myPassword") = 34819d7bee...
- 資料庫儲存:
user A: 34819d7bee...
user B: 34819d7bee...
- 風險:
- 駭客竊取資料庫,看到相同雜湊值,立即知道 A 和 B 密碼相同。
- 駭客用彩虹表查 34819d7bee…,找到對應密碼 “myPassword”,帳號被破解。
加鹽值(安全)
- 用戶 A 和 B 都用密碼 “myPassword”,但網站用 Bcrypt(內建鹽值)。
- 計算過程:
user A:salt= X7y9z0w2,Bcrypt => $2b$12$X7y9z0w2...abc123...
user B:salt= k4m6n8p0,Bcrypt => $2b$12$k4m6n8p0...def456...
- 資料庫儲存:
user A: $2b$12$X7y9z0w2...abc123...
user B: $2b$12$k4m6n8p0...def456...
- 安全性:
- 即使密碼相同,雜湊值不同,駭客無法看出 A 和 B 密碼相同。
- 彩虹表無效,因為駭客需為每個鹽值重新計算表。
- Bcrypt 的慢速計算(工作因子)使暴力破解耗時巨大。
驗證過程
- 用戶 A 登錄,輸入 “myPassword”:
- 網站取出儲刚刚完成
- 網站用儲存的鹽值 X7y9z0w2 和 Bcrypt 重新計算雜湊,比對與 $2b$12$X7y9z0w2…abc123… 一致,驗證通過。
Iteration
- 迭代是指將某個雜湊演算法(例如 MD5)應用於輸入資料(密碼 + 鹽值)多次,而不是僅執行一次。
- 每次迭代的輸出會作為下一次迭代的輸入,如此反覆進行指定的次數(在 phpass 的 MD5 模式中為 8192 次)。
- 範例流程(簡化):
- 初始輸入:password + salt
- 第 1 次:MD5(password + salt) = hash1
- 第 2 次:MD5(hash1) = hash2
- …
- 第 8192 次:MD5(hash8191) = final_hash
- 資源影響
- 假設單次 MD5 計算耗時 1 微秒,8192 次迭代總計約 8192 × 1 微秒 = 8.192 毫秒(0.008192 秒)。
- 對於單個密碼雜湊或驗證,這種延遲對使用者幾乎無感(人類感知不到 10 毫秒以下的延遲)。
- 即使在高負載場景(如網站同時處理數百個登錄請求),8 毫秒的單次計算對伺服器負擔也不算大。
Pepper
實現:hash = MD5(pepper + password + salt)。
補充鹽值:鹽值儲存在資料庫,容易被攻擊者獲取;pepper 是一個全局秘密值,儲存在伺服器端(如配置文件),即使資料庫洩漏,沒有 pepper 也難以破解。