談到密碼雜湊,一定會出現「salt(鹽)」。在做雜湊前加入一個隨機值,安全性就會大不相同。本文講解其機制與要點(不會寫出攻擊步驟)。
為什麼 salt 能讓密碼更安全
沒有 salt 時,密碼相同的人保存的雜湊也相同。這給了攻擊者兩種手段:用預先計算好的對照表(彩虹表)直接查出密碼,以及把一個人的破解結果套用到所有密碼相同的人身上。
加上 salt 後,即便同樣是 "password123",每個使用者的保存值也會完全不同。
出於同樣的道理,salt 必須每個使用者(最好是每個密碼)都重新建立。如果所有人共用同一個 salt,相同密碼的人之間保存值又會一致,好不容易得來的效果就被削弱了。產生時要使用難以猜測的密碼學亂數,建立好的 salt 要和雜湊值一起保存(無需保密)。
另外,salt 守護的範圍僅限於「預先計算好的表」和「針對重複使用密碼的批次破解」。針對單個密碼進行窮舉的攻擊本身,讓它變慢並不是 salt 的工作,而是由 bcrypt/Argon2 這類故意做得很慢的雜湊來承擔。兩者的守備範圍不同,所以必須搭配使用。
salt 的長度與產生方式
有兩份文件訂出了基準:
- NIST SP 800-63B-4(美國的認證指引):salt 至少 32 位元,且要讓保存的 salt 之間幾乎不會重複。每個密碼都要保存 salt 與雜湊
- RFC 9106(Argon2 的規格):密碼雜湊建議使用 16 位元組(128 位元),空間不足時可縮短到 64 位元
產生時的三條規則:
- 使用密碼學亂數產生器。請使用程式語言提供的安全亂數函式,而不是
Math.random()或 PHP 的mt_rand()這類可預測的函式 - 不要使用可被猜到的值:使用者名稱、電子郵件地址、使用者 ID 或註冊時間,都不能代替 salt
- 變更密碼時產生新的 salt。沿用舊的 salt,會讓新舊雜湊之間留下關聯
實務上,標準的 bcrypt、Argon2 與 scrypt 實作會自動滿足以上所有條件。如果你正在寫自行產生 salt 的程式碼,請把它當成需要檢視的訊號。
salt、pepper 與金鑰延展的區別
密碼保存有三個名稱相近的措施。它們防禦的對象不同,任何一個都無法取代另一個。
| salt | pepper | 金鑰延展(慢速雜湊) | |
|---|---|---|---|
| 是什麼 | 每個使用者的隨機值 | 所有使用者共用的祕密值 | 故意讓雜湊計算變得很耗費資源 |
| 是祕密嗎? | 不是(和雜湊一起保存) | 是(保存在資料庫之外) | 設定值不需要保密 |
| 防禦的對象 | 預先計算的表、相同密碼的批次破解 | 只有資料庫外洩時的破解 | 對每個雜湊的暴力破解 |
| 實作 | bcrypt/Argon2 自動加上 | 有需要時自己加上 | bcrypt 的 cost、Argon2 的記憶體與迭代次數 |
NIST SP 800-63B-4 建議,「只有驗證者知道的祕密金鑰」(通常稱為 pepper)要與密碼雜湊分開保存,最好保存在 HSM 等專用硬體內。
實際案例:LinkedIn 未加 salt 的保存方式(2012)
2012 年 6 月,據報約 650 萬筆據稱屬於 LinkedIn 使用者的密碼雜湊被張貼到網路上。LinkedIn 確認其中部分對應到 LinkedIn 帳號,並讓受影響會員的密碼失效。
該公司在官方部落格上表示,它一直在從「只對密碼做雜湊」的系統,轉移到「同時做雜湊與加 salt」的系統。當時,媒體引述的專家批評該公司只用 SHA-1 雜湊來保護密碼。
兩點教訓:
- 沒有加 salt 的雜湊,可以用預先計算的表把常見的密碼成批還原
- 即使正在轉移中,只要還留著舊格式的雜湊,外洩時的影響就由那個舊格式決定
如何檢查自己的環境
看保存字串的形狀
在你能管理的開發用資料庫中,看幾筆密碼的值。若是 bcrypt,$2b$12$ 之後的 22 個字元就是 salt。若是 Argon2,$argon2id$v=19$m=…,t=…,p=…$ 之後是以 $ 分隔的 salt 與雜湊。如果值不符合任何一種形狀,也沒有另外的 salt 欄位,密碼可能是沒有加 salt 保存的。
用相同密碼建立兩個測試帳號
在開發環境中,用相同的密碼註冊兩個測試帳號,比較保存的值。如果兩者一致,就表示 salt 沒有發揮作用。也確認變更密碼時 salt 的部分會改變。
找出手動組出 salt 的程式碼
在程式碼中搜尋 salt 這個字,以及手動組合亂數或雜湊函式的地方。如果發現可預測的亂數函式、沿用使用者名稱或 ID、或固定字串,請改用程式語言或框架提供的標準密碼函式。
轉移時參考實務指南
如何從未加 salt 的保存方式轉移(例如在登入時重新雜湊),以及各演算法的建議設定,請見密碼的安全保存方法。
常見誤解與正解
誤解
- salt 必須保密
- 所有使用者用共同的 salt 就行
- 只要加上 salt,弱密碼也安全
正解
- salt 以公開為前提即可(和雜湊一起保存)
- 每個使用者(最好是每個密碼)用不同的亂數
- salt 是針對「預先計算與重複使用密碼破解」的對策。窮舉對策由慢速雜湊承擔
本站的觀點:先把 salt 與慢速雜湊做對
pepper 是針對「只有資料庫被偷」這種情況的額外措施。它增加了在別處管理祕密的工作,而且一旦遺失那個祕密,就再也無法驗證任何人的密碼。
所以實務上,第一優先是用 bcrypt/Argon2 正確地使用salt+慢速雜湊。等你已經有金鑰管理的機制之後,再考慮 pepper。
出處(一手資料)
- NIST「SP 800-63B-4 Digital Identity Guidelines: Authentication and Authenticator Management」(2025 年 8 月)— pages.nist.gov(salt 至少 32 位元、保存 salt 與雜湊、分開保存的祕密金鑰)
- RFC 9106「Argon2 Memory-Hard Function for Password Hashing and Proof-of-Work Applications」— rfc-editor.org(密碼雜湊建議使用 16 位元組的 salt)
- OWASP「Password Storage Cheat Sheet」— cheatsheetseries.owasp.org(加 salt、pepper、多數函式庫會自動處理 salt)
- PHC string format(Argon2 等使用的雜湊字串格式)— github.com/P-H-C
- TechCrunch「LinkedIn Confirms Hack And Leak Of 'Some' User Passwords」(2012 年 6 月 6 日)— techcrunch.com(約 650 萬筆、該公司關於轉移到雜湊加 salt 的聲明)
- Computerworld「LinkedIn provides breach update -- sort of」(2012 年 6 月 8 日)— computerworld.com(對只用 SHA-1 雜湊的批評、密碼失效)
接下來閱讀
- 術語:密碼雜湊是什麼(與 salt 成對理解)
- 對策:密碼的安全保存方法(雜湊+salt 實踐)
- 入門:密碼的正確保管