前面我們學了雜湊、編碼、加密這三個觀念,知道它們的特性不同:
- 雜湊:單向,回不去
- 編碼:雙向,不需要鑰匙
- 加密:雙向,需要鑰匙
但知道歸知道,實際上什麼時候該用哪一個?
這篇文章用一個最常見的情境——密碼儲存,來讓你理解這三個觀念到底該怎麼選。
密碼儲存的問題
假設你在做一個會員系統,使用者註冊的時候會設定密碼。
問題來了:這個密碼要怎麼存進資料庫?
網路上有很多討論,有人說要用編碼存,有人說要用加密存,有人說要用雜湊存。
到底哪個才對?我們一個一個來想。
方案一:用編碼儲存密碼
做法
假設使用者的帳號是 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...把雜湊後的值存進資料庫。
那忘記密碼怎麼辦?
工程師也沒辦法幫你找回密碼,因為根本無法從雜湊值還原出原始密碼。
所以現在大部分網站的「忘記密碼」功能,都是給你一個連結,讓你重新設定新密碼,而不是把舊密碼寄給你。
你的舊密碼,永遠不會有人知道。
那怎麼驗證登入?
使用者登入的時候,系統會:
- 把使用者輸入的密碼做雜湊
- 比對這個雜湊值跟資料庫裡存的雜湊值是不是一樣
- 一樣就放行,不一樣就拒絕
使用者輸入:hello123
↓
雜湊處理
↓
5d41402abc4b2a76...
↓
跟資料庫的值比對 ──→ 一樣就登入成功結論:密碼應該用雜湊來儲存。
常見問題:為什麼系統知道我用過同樣的密碼?
有些網站在你換密碼的時候會說:「這個密碼跟你上個月用的一樣,請換一個。」
你可能會想:「它是不是偷偷記住我的密碼了?」
不是的。
系統存的是你過去幾次密碼的雜湊值。
當你輸入新密碼,系統會把它雜湊一次,然後跟過去的雜湊值比對。如果一樣,就知道你用過這個密碼。
它從頭到尾都不知道你的原始密碼是什麼。
雜湊也有弱點:彩虹表攻擊
前面講過,雜湊理論上是不可逆的,但可以用「查表」的方式破解。
如果你用的是簡單的 Hash Function(像 MD5),網路上已經有超大的彩虹表可以查。攻擊者拿到你的雜湊值,查一下表就知道原始密碼了。
那怎麼辦?
強化雜湊的三個技巧
既然雜湊有被彩虹表破解的風險,我們就要想辦法讓它更難被破解。
技巧一:用更強的演算法
為什麼 MD5 不行?
MD5 是很早期的雜湊演算法,當時設計的時候電腦運算能力還沒那麼強。
但現在不一樣了,MD5 有兩個致命問題:
- 產生的雜湊值太短:只有 128 位元,可能的組合相對較少
- 網路上已經有超大的彩虹表:常見密碼的 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… |
即使小明和小華用了一模一樣的密碼,因為鹽不同,最後存進資料庫的雜湊值也完全不同。
這表示攻擊者沒辦法說「我破解了一個人的密碼,其他用同樣密碼的人也一起破解」。他必須一個一個破解,成本大幅增加。
鹽要存在哪裡?
鹽通常會跟雜湊值一起存在資料庫裡(因為驗證的時候需要用到)。
你可能會想:「鹽也存在資料庫,攻擊者不是也能看到?」
沒錯,但這不影響安全性。因為:
- 攻擊者需要針對每個鹽重新計算彩虹表,工作量爆炸
- 他不知道鹽加在密碼的哪個位置(前面?後面?中間?)
- 就算知道位置,每個人的鹽都不同,沒辦法批量破解
技巧三:重複雜湊(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 傳輸
- 私密訊息傳遞
- 檔案加密
特性:雙向,需要鑰匙才能解開
注意:能用雜湊的地方,通常不適合用加密或編碼。
重點整理
- 密碼要用雜湊儲存,不是編碼,也不是加密
- 編碼沒有安全性,任何人都能解開
- 加密需要鑰匙,但鑰匙可能外洩,內部人員有機會看到所有密碼
- 雜湊是單向的,連工程師都無法知道原始密碼
- 加鹽 + 重複雜湊,可以大幅增加破解難度
- 忘記密碼只能重設,不能找回,這才是正確的做法
最後
這篇文章用密碼儲存這個例子,讓你理解雜湊、編碼、加密在實務上的差別。
記住這個原則:
- 要保護但不需要還原 → 用雜湊
- 要轉換格式 → 用編碼
- 要保護且需要還原 → 用加密
搞懂這些,你在做權限驗證的時候就會更有方向了!