「使用者的密碼,到底該怎麼存進資料庫才對?」——這是每個做服務的人都必然會撞上一次的問題。答案很明確。我們不涉及攻擊手法,只按順序講清楚安全的儲存方法。
為什麼明文、加密、裸雜湊都不行
我們按資料庫遲早會外洩這個前提來考慮。一旦外洩,不同的儲存方式造成的損失天差地別。
- 明文:外洩的瞬間所有密碼一覽無遺。再加上重複使用同一密碼,使用者在其他服務上的帳號也會被連鎖攻陷。最糟。
- 加密(可逆):有金鑰就能還原=金鑰一起外洩就等同於明文。密碼本就沒有讀回來的必要,因此可逆這件事本身毫無意義。
- 裸雜湊(MD5/SHA-256):快速雜湊讓攻擊者能高速嘗試海量候選。常見密碼會被彩虹表或暴力破解還原出來。
走到安全為止的「四個階段」
把它理解成給弱方式一步步疊加對策的過程,會很快上手。
要點在於,鹽和慢雜湊的職責是不同的。鹽負責讓「預計算(彩虹表)和批次破解重複密碼」失效。慢雜湊負責把「暴力破解的速度壓到不現實的水準」。兩者齊備,才第一次稱得上安全(→ 什麼是雜湊化)。
實務:現在就該做的事
使用 Argon2id(或 bcrypt)
新專案把 Argon2id 作為首選。它還能要求消耗記憶體,因此對 GPU 暴力破解很有抵抗力。如果更看重成熟穩定的實作,bcrypt 在實務中也完全夠用。兩者都直接使用標準函式庫的實作。
鹽交給「自動」去做
bcrypt/Argon2 會為每個使用者自動產生鹽,並把它一併塞進雜湊字串裡。你不需要自己另存一欄鹽。手動去拼 MD5(salt + password),正是不該犯的典型錯誤。
按執行環境設定成本(強度)
把 bcrypt 的成本係數、Argon2 的記憶體・迭代・並行度,設到正常登入在體感上察覺不到延遲的範圍內的最大值。伺服器越快,攻擊者也越快,所以要以年為週期複核並上調。
驗證用標準的比對函式
登入時,把已存雜湊與輸入用函式庫提供的比對函式進行比對(很多實作已經做成了對時序差有所防範的常數時間比較)。不要自己寫字串相等判斷。
弱雜湊用「登入時重新雜湊」來遷移
如果已經用 MD5/SHA-256 存了,就在使用者登入成功的那一刻用新方式重新雜湊並覆寫保存。對於尚未登入的部分,可以用 bcrypt 等把既有雜湊再包一層,用這種雙重雜湊暫時提升整體強度。
常見的錯誤做法 vs 正確的實作
常見錯誤
- 把密碼加密後儲存(金鑰一外洩就全完)
- 直接存
MD5(password)或SHA-256(password) - 所有使用者共用同一個鹽
- 自己手動拼
hash(salt + password)
正確的實作
- 用 Argon2id / bcrypt 做單向雜湊
- 鹽按每個使用者分配(由函式庫自動附加)
- 成本設在體感可接受範圍內的最大值,並定期上調
- 直接使用標準的雜湊/比對函式
本站的觀點:密碼儲存是該踩在「成熟正解」上的地方
密碼儲存不是一個可以彰顯獨創性的領域。老老實實踩在經過全球驗證的 Argon2/bcrypt 標準實作上,既最安全,維運也最省心。本站的立場是「在這裡不要搞創意發揮」。真正該投入精力的,是從根本上減少對密碼的依賴——把多因素驗證(MFA)做到位,以及未來向通行密鑰(無密碼)遷移。再強的雜湊,也兜不住弱密碼和重複使用。
延伸閱讀
- 術語:什麼是密碼雜湊化 / 什麼是鹽
- 入門:密碼的正確保管(使用者側) / 如何挑選密碼管理器
- 對策:如何挑選兩步驟驗證(MFA)(減少對密碼的依賴)
- 術語:什麼是通行密鑰(向無密碼遷移)
FAQ
Q密碼加密後再儲存不就行了嗎?
不行,加密並不合適。加密只要有金鑰就能還原(解密),金鑰一旦外洩,所有密碼就會被還原成明文。密碼對維運方來說也沒有讀回來的必要,因此無法還原的『雜湊化』才是正解。不過裸雜湊還不夠,需要再配合為每個使用者加鹽以及慢雜湊(bcrypt/Argon2)。
Qbcrypt 和 Argon2,到底該用哪個?
如果是新專案,Argon2(尤其是 Argon2id)是首選。因為它的記憶體佔用和計算量都可調,能很好地抵禦 GPU 暴力破解。如果更看重既有資產或成熟穩定的實作,bcrypt 在實務中也完全夠用。關鍵在於:無論選哪個,都要『直接使用標準函式庫的實作,絕不自己拼裝』。
Q我已經用 MD5 或 SHA-256 存了密碼,該怎麼遷移?
沒法把它們一次性還原成明文(這恰恰是它的優點),所以要在登入時遷移。當使用者成功登入的那一刻,用新方式(如 Argon2id)對輸入的正確密碼重新雜湊並覆寫保存。對於一直沒登入的使用者,還可以用 bcrypt 等把既有雜湊『再包一層』,用這種雙重雜湊暫時提升一下整體強度。