密碼雜湊(Password Hashing)是一種「單向運算」,把密碼丟進演算法裡變成一串無法反推的亂碼。
加鹽(Salting)是在雜湊前加入隨機字串,讓相同密碼產生完全不同的結果,防止彩虹表攻擊。
建議使用 Argon2id;若需要符合 FIPS 標準,可選 PBKDF2;舊系統仍可維持 bcrypt。
這幾年大型外洩事件層出不窮:Yahoo(2013)、Adobe(2013)、Facebook(2019)、LinkedIn(2021)⋯一次資料外洩,可能讓公司名譽、營收、信任度都直接歸零。
雖然現在越來越多網站導入像生物辨識登入(Biometric Login)、WhatsApp / Telegram OTP 但「帳號 + 密碼」仍然是大多數應用的基本登入方式。
這也代表開發者一定要知道怎麼安全地儲存密碼。
這篇文章會帶你快速理解:
- 什麼是「密碼雜湊(Hashing)」
- 什麼是「加鹽(Salting)」
- 各演算法差別在哪、該怎麼挑、該怎麼設定
什麼是密碼雜湊(Password Hashing)
當使用者註冊帳號時,資料庫不應該直接儲存明文密碼。
舉例來說,如果你在 Netflix 註冊:
username: netflix_user1
password: iLoveWatch1ngCom3dy
這個密碼不會原封不動地進資料庫。
開發者會先用雜湊演算法(hashing algorithm)把它轉成一串亂碼,例如用 bcrypt:
$2a$12$CpzXhh5bswd2gNd1eFTNnugGTE8CWUIgpzPivCejVk7JN284V0g96
這串結果就是「hash」。
再來,如果你把第一個 i 改成 u,整個結果就完全不一樣:
$2a$12$E0V50LSt1hUErz3d05f.ruME6K/.y3YIjRLJOKZyCGmuO7feO.xC.
小小改動就能讓整個雜湊值天差地遠。
因為 hashing 是單向的,你可以從密碼算出 hash,但無法從 hash 還原出密碼。 這跟「加密(encryption)」不一樣 —— 加密可以解回原文,hash 完全不行。
實際登入流程
使用者登入時:
- 系統用相同演算法重新 hash 使用者輸入的密碼
- 拿結果去比對資料庫裡的 hash
- 若相同,驗證成功
即使駭客拿到資料庫,也只能拿到一堆 hash,完全沒辦法直接登入帳號。
常見的密碼雜湊演算法
密碼雜湊演算法(hash algorithm)是一種「單向函式」,會把輸入字串轉成固定長度的亂碼。理想狀況下,不同輸入應該產生完全不同的輸出,而且運算要夠慢,才能抵擋暴力破解(brute-force attack)。
在早期,開發者常使用像 MD5、SHA-1、SHA-2 這類演算法。但這些演算法的問題是:「太快了」。對駭客來說,越快代表越容易大量嘗試密碼。
現在我們已經知道這些通用雜湊演算法(尤其是 MD5、SHA-1)都不適合拿來儲存密碼。它們仍可用於資料完整性驗證,但不適用於密碼安全。
根據 OWASP Password Storage Cheat Sheet,目前建議使用專為密碼儲存設計的演算法: Argon2id、bcrypt、PBKDF2、scrypt。
🥇 Argon2id — 2025 年的預設首選
Argon2 是 2015 年 Password Hashing Competition(PHC)冠軍。
它有三種版本:
- Argon2d(偏向防 GPU 攻擊)
- Argon2i(偏向防 side-channel 攻擊)
- Argon2id(兩者混合、最平衡)
👉 所以在 2025 年,Argon2id 幾乎是所有新系統的預設選擇。
Argon2id 可調整參數包含:
- 記憶體使用量(memory)
- 迭代次數(iterations / time cost)
- 平行運算數量(parallelism)
- salt 長度(salt length)
- 雜湊輸出長度(key length)
這些參數讓你能依照硬體與安全等級自由微調:
越多記憶體與迭代 → 破解成本越高。
bcrypt — 老牌但仍穩定的選擇
bcrypt 是 1999 年由 Niels Provos 與 David Mazieres 設計,基於 Blowfish 加密演算法。它的設計重點是「可調慢速化」。bcrypt 會自動幫密碼加 salt,並允許你設定「cost factor(迭代次數)」。成本越高,雜湊越慢,駭客就越難暴力破解。
目前 bcrypt 仍廣泛使用,但要注意幾點:
- 只處理前 72 bytes 的輸入,太長會被截斷
- 不支援「記憶體強度(memory-hard)」設計
- 適合舊系統維護,不建議新專案採用
PBKDF2 — 符合 FIPS 標準的傳統強者
PBKDF2(Password-Based Key Derivation Function 2)是由 NIST(美國國家標準技術研究院)推薦的演算法, 在 FIPS-140 認證環境中仍被廣泛使用。它透過反覆進行 HMAC(例如 SHA-256)運算來延長破解時間。
運算速度雖然不如 Argon2id 慢,但在受法規約束的金融或政府系統中仍是安全選項。
scrypt — Argon2id 的次佳替代
如果你所在的環境無法使用 Argon2id,那麼 scrypt 是個不錯的替代方案。它同樣具備記憶體強度(memory-hard)特性,能有效防止 GPU 攻擊。
推薦安全參數參考值(起始設定)
- Argon2id
- m=19–46 MiB, t=2, p=1
- 每次驗證時間 ≈ 1 秒內
- scrypt
- N=2^17, r=8, p=1
- bcrypt
- cost ≥ 10
- 注意:不支援超過 72-byte 輸入
- PBKDF2-HMAC-SHA-25
- ≥ 600,000 iterations
實務上,建議在接近生產環境的機器上測試(benchmark), 讓「單次驗證」大約落在 0.5~1 秒左右, 確保既安全又不會拖慢登入體驗。
密碼雜湊的限制與常見攻擊手法
雜湊(Hashing)雖然能大幅提升密碼安全,但它不是萬能的防線。在現實世界中,駭客仍有幾種方式能試著「猜出」原始密碼。 我們來看幾個常見的攻擊手法,以及為什麼它們有效。
1. 暴力破解(Brute-Force Attack)
這是最直接、也是最笨但永遠存在的攻擊手法。駭客會使用電腦不斷嘗試各種密碼組合,把每一組都跑進同樣的 hash 函式裡, 直到某一次的結果跟資料庫裡的 hash 對上。
雖然這個過程理論上可行,但實際上很花時間。像 bcrypt、Argon2id、PBKDF2 這類密碼雜湊演算法都是「刻意設計得很慢」,目的就是讓這種暴力破解變得極度沒效率。
但要注意,時間只是問題。如果演算法設定得太輕(例如 bcrypt cost 太低、Argon2id 記憶體太少), 駭客還是能用 GPU 叢集在合理時間內跑出結果。
2. 彩虹表攻擊(Rainbow Table Attack)
彩虹表其實就像是駭客版的「查表法」。他們會事先建立一個巨大的資料表,把常見密碼的雜湊結果都算好。 只要拿到你的資料庫,他們就能比對看看哪些 hash 跟彩虹表裡的結果一樣。
如果兩個相同密碼產生的 hash 也是相同的,那這場戰就幾乎結束了。
什麼是「加鹽」(Salting)?
你可能以為我們要做菜(hash brown + salt 😆),但在密碼學裡,「salting」其實是一個非常重要的安全概念。
簡單來說:加鹽(Salting)就是在密碼雜湊(Hashing)之前,加上一段隨機亂數字串(Salt)。
這段字串可以是 16~32 bytes 長,由加密安全的隨機函式(CSPRNG)產生。
這樣做有兩個關鍵好處:
- 相同的密碼會產生不同的雜湊結果
- 彩虹表攻擊(Rainbow Table Attack)幾乎完全失效
為什麼加鹽這麼重要?
如果兩個人不小心用了同一組密碼,例如 Michael 和 Bob 都用 s@1t3dH@shBrown,那他們的 bcrypt 雜湊值就會一模一樣:
$2a$12$xdWgQ5mhv8rSaUK3qdusTO4XdMFbQi6TD/1VvOZjvGm10RXnhZZa2
駭客只要比對出重複的 hash,就能立刻知道哪些人用了相同密碼。這在真實外洩資料庫中非常常見。
但如果你在雜湊前先各自加入不同的 salt:
- Michael 的 salt:
Iwx2ZE - Bob 的 salt:
0DoVej
則他們的輸入就變成:
Michael → Iwx2ZEs@1t3dH@shBrown
Bob → 0DoVejs@1t3dH@shBrown
最終生成的雜湊結果會完全不同。這樣即使兩人密碼一樣,資料庫裡的 hash 也完全對不上。
加鹽後的驗證流程
在實務上,系統會把 salt、hash、username 放在同一筆紀錄中。
使用者登入時,流程大致如下:
- 查詢該使用者的 salt
- 用相同演算法把輸入的密碼 + salt 一起 hash
- 比對結果是否與資料庫裡的 hash 一致
這樣就能確認密碼正確。
Salting 最佳實踐
1️⃣ 每個使用者都要有自己的 salt
千萬不要整個系統共用一個「global salt」,那沒什麼防禦力。
2️⃣ Salt 必須用 CSPRNG(cryptographically secure random number generator)產生 不要用 Math.random() 或時間戳記之類可預測的亂數。
3️⃣ Salt 長度建議至少與 hash 長度相同(通常 16–32 bytes)
4️⃣ 不要把使用者名稱或 ID 拿來當 salt 太容易被猜到。
5️⃣ Salt 不是秘密,可以和 hash 一起儲存
這點很多人誤會了——salt 不用加密, 因為它的安全性不是靠「藏起來」, 而是靠「讓每個使用者的 hash 結果都不同」。
bcrypt 與 Argon2id 已經自動加鹽
現代演算法像 bcrypt、scrypt、Argon2id 其實已經內建 salting 機制。它們會在產生 hash 時自動生成 salt,並把 salt 與雜湊結果包在同一段字串裡。
也就是說:當你使用現代函式庫(例如 bcrypt 或 Argon2id)時,不需要自己額外加鹽。但了解 salting 的原理,仍然非常重要,因為它讓你明白為什麼不同使用者的 hash 永遠不能相同。
Salting 實作範例(bcrypt 版本)
import bcrypt from "bcryptjs";
const password = "s@1t3dH@shBrown";
const saltRounds = 12;
// 自動產生 salt 並 hash
const hash = bcrypt.hashSync(password, saltRounds);
console.log("hash:", hash);
// 驗證密碼
const isValid = bcrypt.compareSync(password, hash);
console.log("valid:", isValid);
你不需要自己管理 salt,bcrypt 幫你全都包好了。
常見錯誤
❌ 把 salt 寫死在程式碼裡(例如 const SALT = "123456")這樣所有人都會有相同的 salt,形同虛設。
❌ 自己拼接 salt 字串:應用內建函式庫,不要自己串字串,容易產生不可預期結果。
❌ 沒存 salt:如果演算法沒自動處理(例如自己用 SHA256), 忘記存 salt 會導致之後完全無法驗證密碼。
Hash vs Salt vs Pepper
- Hash 是一種「單向轉換」,用來驗證密碼。
- Salt 是在雜湊前為每個使用者加上的隨機亂數。
- Pepper 則是整個系統共用的秘密字串,另外安全儲存在應用層。
簡單來說:每個密碼都應該「hash + salt」,如果要再多一層防禦,可以在應用層再加上 pepper。
常見問題(FAQ)
Q1. Salting 跟 Hashing 一樣嗎?
不一樣。
- Hashing:把密碼單向轉成亂碼。
- Salting:在 hash 前加入隨機值,確保每個 hash 都不同。
Q2. 用 bcrypt 還需要自己加 salt 嗎?
不需要。現代的 bcrypt 實作會自動產生 salt 並內嵌在 hash 裡。 只要使用標準函式庫,bcrypt 會幫你全套搞定。
Q3. 那 Pepper 呢?要怎麼放?
Pepper 是整個系統共用的一段秘密字串,用來增加防禦層。應該放在安全的地方,例如:
- AWS KMS / Google Cloud KMS
- HSM
- 環境變數(.env,不進版本控制)
絕對不要放在同一個資料庫或程式碼裡。
Q4. 2025 年最建議用哪個演算法?
首選是 Argon2id,安全、可調整、支援 memory-hard。若你有法規需求(例如政府或金融單位),則用 PBKDF2-HMAC-SHA-256。bcrypt 僅建議用在舊系統或框架限制下。
Q5. Argon2id 安全參數該怎麼設?
起始建議值如下:
- 記憶體(m):約 19–64 MiB
- 迭代(t):1–3
- 平行度(p):1
並確保單次驗證時間落在 0.5~1 秒內, 根據實際硬體做 benchmark。
Q6. 可以只用 SHA-256 / SHA-3 嗎?
不建議。通用雜湊演算法太快了,無法有效抵禦暴力破解。 請使用專為密碼設計的演算法:Argon2id、scrypt、bcrypt、PBKDF2。
Q7. Salt 要多長?
建議使用 16~32 bytes(≥128 bits)的隨機字串,由 CSPRNG 產生(例如 crypto.randomBytes)。
Q8. Salt 要存哪裡?
Salt 不是秘密,可以和 hash 一起存在同一欄位或同一筆紀錄中。bcrypt / Argon2id 會自動把它包在 hash 裡。
Q9. bcrypt 為什麼有 72-byte 限制?
bcrypt 在設計上只取前 72 bytes 的輸入,多餘會被截斷。建議限制密碼長度或改用 Argon2id。 如果真的要處理超長密碼,可用「HMAC + pepper」的 pre-hash 策略,但請小心實作。
Q10. 要怎麼從 bcrypt / PBKDF2 遷移到 Argon2id?
使用「opportunistic rehashing」策略:
- 登入時先用舊演算法驗證。
- 若成功,立即用 Argon2id 重新雜湊密碼並更新。
- 保留舊驗證機制一段時間,直到使用者逐漸被遷移完。
Q11. 什麼時候應該重新雜湊(rehash)?
當以下情況發生:
- 你提高 Argon2id 的 memory/time 設定。
- 你改用新演算法(如從 bcrypt → Argon2id)。
- 你改變 pepper 或安全政策。
每個帳號都應有一個 hash_version 欄位方便追蹤。
Q12. 密碼應該「加密」還是「雜湊」?
應該 雜湊(hashing),不要加密。加密是可逆的,設計來「取回資料」; 雜湊是單向的,用來「驗證一致性」。 密碼從不該被還原。
用 Authgear 讓密碼安全更簡單
Authgear 內建的密碼儲存機制使用 Argon2id,自動完成:
- 加鹽(Salt generation)
- 安全雜湊(Hashing)
- 參數版本控管
- 自動 rehash
- 整合多重驗證(2FA / Passkey / WebAuthn)
你不需要重新設計密碼流程,就能同時擁有「強安全」與「好用戶體驗」。
想了解如何整合?聯絡我們 了解如何讓你的應用升級到企業級安全,提升轉換率、降低維護風險。










