密碼到底該怎麼存?雜湊、編碼、加密的實戰應用

Published December 11, 2025 by 徐培鈞
基礎概念

前面我們學了雜湊、編碼、加密這三個觀念,知道它們的特性不同:

  • 雜湊:單向,回不去
  • 編碼:雙向,不需要鑰匙
  • 加密:雙向,需要鑰匙

但知道歸知道,實際上什麼時候該用哪一個?

這篇文章用一個最常見的情境——密碼儲存,來讓你理解這三個觀念到底該怎麼選。

密碼儲存的問題

假設你在做一個會員系統,使用者註冊的時候會設定密碼。

問題來了:這個密碼要怎麼存進資料庫?

網路上有很多討論,有人說要用編碼存,有人說要用加密存,有人說要用雜湊存。

到底哪個才對?我們一個一個來想。

方案一:用編碼儲存密碼

做法

假設使用者的帳號是 john,密碼是 hello123

我們用 Base64 編碼把密碼轉換一下:

hello123 ──→ Base64 Encode ──→ aGVsbG8xMjM=

然後把 aGVsbG8xMjM= 存進資料庫。

看起來好像可以?

如果使用者忘記密碼,工程師只要去資料庫把 aGVsbG8xMjM= 拿出來,用 Base64 解碼:

aGVsbG8xMjM= ──→ Base64 Decode ──→ hello123

就可以把原始密碼找回來,寄給使用者。

問題在哪?

這超級不安全!

因為編碼是雙向的,任何人只要拿到資料庫裡的資料,都可以輕鬆解碼出原始密碼。

更可怕的是,很多人會在不同網站使用同一組密碼。如果你的密碼被解出來,搞不好連你的銀行帳戶、社群帳號都會被盜。

結論:編碼不能用來存密碼。

方案二:用加密儲存密碼

做法

既然編碼不行,那我們用加密總可以吧?加密需要鑰匙,沒有鑰匙就解不開。

假設我們用 AES 加密,搭配一把鑰匙:

hello123 + 鑰匙 ──→ AES Encrypt ──→ X7f9k2mN...

把加密後的結果存進資料庫。

看起來安全多了?

如果使用者忘記密碼,工程師用同一把鑰匙解密:

X7f9k2mN... + 鑰匙 ──→ AES Decrypt ──→ hello123

就可以找回原始密碼。

問題在哪?

問題出在那把鑰匙

這把鑰匙一定要存在某個地方,讓系統可以用它來驗證密碼。但這也表示:

  • 公司內部的工程師有機會拿到這把鑰匙
  • 只要有鑰匙,就可以解開所有會員的密碼
  • 如果有人心懷不軌,後果不堪設想

結論:加密也不適合用來存密碼。

方案三:用雜湊儲存密碼

做法

雜湊是單向的,資料進去就回不來了。

hello123 ──→ Hash Function ──→ 5d41402abc4b2a76...

把雜湊後的值存進資料庫。

那忘記密碼怎麼辦?

工程師也沒辦法幫你找回密碼,因為根本無法從雜湊值還原出原始密碼

所以現在大部分網站的「忘記密碼」功能,都是給你一個連結,讓你重新設定新密碼,而不是把舊密碼寄給你。

你的舊密碼,永遠不會有人知道。

那怎麼驗證登入?

使用者登入的時候,系統會:

  1. 把使用者輸入的密碼做雜湊
  2. 比對這個雜湊值跟資料庫裡存的雜湊值是不是一樣
  3. 一樣就放行,不一樣就拒絕
使用者輸入:hello123
         
      雜湊處理
         
   5d41402abc4b2a76...
         
  跟資料庫的值比對 ──→ 一樣就登入成功

結論:密碼應該用雜湊來儲存。

常見問題:為什麼系統知道我用過同樣的密碼?

有些網站在你換密碼的時候會說:「這個密碼跟你上個月用的一樣,請換一個。」

你可能會想:「它是不是偷偷記住我的密碼了?」

不是的。

系統存的是你過去幾次密碼的雜湊值

當你輸入新密碼,系統會把它雜湊一次,然後跟過去的雜湊值比對。如果一樣,就知道你用過這個密碼。

它從頭到尾都不知道你的原始密碼是什麼。

雜湊也有弱點:彩虹表攻擊

前面講過,雜湊理論上是不可逆的,但可以用「查表」的方式破解。

如果你用的是簡單的 Hash Function(像 MD5),網路上已經有超大的彩虹表可以查。攻擊者拿到你的雜湊值,查一下表就知道原始密碼了。

那怎麼辦?

強化雜湊的三個技巧

既然雜湊有被彩虹表破解的風險,我們就要想辦法讓它更難被破解。

技巧一:用更強的演算法

為什麼 MD5 不行?

MD5 是很早期的雜湊演算法,當時設計的時候電腦運算能力還沒那麼強。

但現在不一樣了,MD5 有兩個致命問題:

  1. 產生的雜湊值太短:只有 128 位元,可能的組合相對較少
  2. 網路上已經有超大的彩虹表:常見密碼的 MD5 值早就被算好了,查一下就破解

簡單說,MD5 現在就像一把生鏽的鎖,有心人幾秒鐘就能撬開。

該用什麼?

現在至少要用 SHA-256 以上的演算法。

hello123 ──→ MD5    ──→ 32 個字元的雜湊值(太短)
hello123 ──→ SHA-256 ──→ 64 個字元的雜湊值(更長更複雜)

SHA-256 產生的雜湊值更長、更複雜,彩虹表要涵蓋的範圍就更大,破解難度直接翻好幾倍。

技巧二:加鹽(Salt)

什麼是加鹽?

「加鹽」的意思是,在原始密碼的前面或後面,加上一段隨機產生的字串,然後再做雜湊。

這段隨機字串就叫做 Salt(鹽)

原始密碼:hello123
鹽:x9Fk2mQp
加鹽後:x9Fk2mQphello123
雜湊後:a7b3c9d2e5f8...(跟原本完全不一樣)

為什麼加鹽有用?

假設攻擊者用彩虹表查到某個雜湊值,反推出原始資料是 x9Fk2mQphello123

但他會遇到一個問題:他不知道哪一部分是真正的密碼,哪一部分是鹽。

  • x9Fk2mQp + hello123
  • 還是 x9Fk2 + mQphello123
  • 還是整串都是密碼?

他根本無從得知。

每個人的鹽都不一樣

更厲害的是,系統會幫每個使用者產生不同的鹽

密碼hello123
x9Fk2mQp
加鹽後x9Fk2mQphello123
雜湊值a7b3c9…
密碼hello123
Yt3Lp8Kn
加鹽後Yt3Lp8Knhello123
雜湊值f2e8d1…

即使小明和小華用了一模一樣的密碼,因為鹽不同,最後存進資料庫的雜湊值也完全不同

這表示攻擊者沒辦法說「我破解了一個人的密碼,其他用同樣密碼的人也一起破解」。他必須一個一個破解,成本大幅增加。

鹽要存在哪裡?

鹽通常會跟雜湊值一起存在資料庫裡(因為驗證的時候需要用到)。

你可能會想:「鹽也存在資料庫,攻擊者不是也能看到?」

沒錯,但這不影響安全性。因為:

  1. 攻擊者需要針對每個鹽重新計算彩虹表,工作量爆炸
  2. 不知道鹽加在密碼的哪個位置(前面?後面?中間?)
  3. 就算知道位置,每個人的鹽都不同,沒辦法批量破解

技巧三:重複雜湊(bcrypt)

什麼是重複雜湊?

光加鹽還不夠保險,我們還可以把雜湊後的結果再拿去雜湊,重複好幾次。

hello123 ──→ 第一次雜湊 ──→ 結果A
結果A   ──→ 第二次雜湊 ──→ 結果B
結果B   ──→ 第三次雜湊 ──→ 結果C
結果C   ──→ 第四次雜湊 ──→ 結果D
...

每多雜湊一次,攻擊者要破解就要多花一倍的時間。

為什麼這招有效?

想像一下攻擊者要破解你的密碼:

  • 如果只雜湊 1 次:他嘗試 100 萬組密碼,花 1 秒鐘
  • 如果雜湊 1000 次:他嘗試 100 萬組密碼,每組都要算 1000 次,花 1000 秒

當破解一個密碼要花好幾天甚至好幾年,攻擊者就會覺得不划算而放棄。

bcrypt 是什麼?

bcrypt 是一個專門為密碼設計的雜湊演算法,它把「加鹽」和「重複雜湊」包在一起,自動幫你處理好。

使用 bcrypt 的時候,你可以設定一個叫做 cost(成本) 的參數,決定要雜湊幾次。

bcrypt(密碼, cost=10)  // 2^10 = 1024 次雜湊
bcrypt(密碼, cost=12)  // 2^12 = 4096 次雜湊

cost 越高,雜湊次數越多,越難破解,但登入驗證也會越慢。

要雜湊幾次才夠?

沒有標準答案,要看你的系統需求。

一般來說:

  • cost = 10:大約 0.1 秒,適合大部分網站
  • cost = 12:大約 0.3 秒,安全性更高
  • cost = 14 以上:可能會讓使用者等太久

重點是取得安全性使用者體驗的平衡。

如果使用者每次登入都要等 5 秒鐘,他們可能會覺得你的網站很爛。但如果太快,安全性又不夠。

其他類似的演算法

除了 bcrypt,還有一些專門為密碼設計的演算法:

  • scrypt:比 bcrypt 更吃記憶體,對抗專門的破解硬體更有效
  • Argon2:目前被認為是最強的密碼雜湊演算法,2015 年贏得密碼雜湊競賽冠軍

如果你是新專案,可以考慮直接用 Argon2

重要觀念:沒有破解不了的系統

資安的世界裡,沒有 100% 安全的系統。

我們能做的,就是把破解成本拉高,高到攻擊者覺得不划算、不值得。

加鹽、重複雜湊、用更強的演算法,都是在增加破解成本。

什麼情況該用哪個?

最後來總結一下,什麼時候該用雜湊、編碼、加密:

雜湊(Hash)

用途:需要儲存但不需要還原的資料

例如

  • 密碼儲存
  • 資料完整性驗證

特性:單向,無法還原

編碼(Encode)

用途:格式轉換、資料傳輸

例如

  • 把圖片轉成 Base64 嵌入網頁
  • URL 中的特殊字元處理

特性:雙向,沒有保護作用

注意:能用編碼的地方,也可以用加密。但編碼沒有安全性可言。

加密(Encrypt)

用途:保護資料的隱私,但對方需要能解開

例如

  • HTTPS 傳輸
  • 私密訊息傳遞
  • 檔案加密

特性:雙向,需要鑰匙才能解開

注意:能用雜湊的地方,通常不適合用加密或編碼。

重點整理

  1. 密碼要用雜湊儲存,不是編碼,也不是加密
  2. 編碼沒有安全性,任何人都能解開
  3. 加密需要鑰匙,但鑰匙可能外洩,內部人員有機會看到所有密碼
  4. 雜湊是單向的,連工程師都無法知道原始密碼
  5. 加鹽 + 重複雜湊,可以大幅增加破解難度
  6. 忘記密碼只能重設,不能找回,這才是正確的做法

最後

這篇文章用密碼儲存這個例子,讓你理解雜湊、編碼、加密在實務上的差別。

記住這個原則:

  • 保護但不需要還原 → 用雜湊
  • 轉換格式 → 用編碼
  • 保護且需要還原 → 用加密

搞懂這些,你在做權限驗證的時候就會更有方向了!