關於密碼,很重要的觀念是「密碼不可以被明文傳輸,也不可以被明文儲存」。不可以明文傳輸是防止傳輸過程中被中間人擷取,不可以明文儲存則是避免當你的主機被駭時,或是有內部工程師心懷不軌時,密碼不會被直接看光光。
這篇文章會講到以下三個問題,是註冊&登入功能會遇到的問題,懶得看內文的人可以直接拉到最後結論:
- 密碼要怎麼安全的從前端傳送到後端 (註冊、登入)
- 密碼要怎麼儲存在資料庫 (註冊)
- 登入時如何驗證使用者的密碼正確 (登入)
密碼如何從前端傳送後端
HTTPS
這篇文章沒有要講 HTTPS 的原理,所以就簡單帶過。簡單來說 HTTPS 就是在原本不安全的、明文傳輸的、不驗證使用者身分的 HTTP上, 加上 SSL 這個加密協議 (實際上現在使用的技術是升級版的 TLS ,不過出於習慣大家還是會叫 SSL ),SSL 提供內容加密、身份認證、資料完整性校驗,因此在 HTTPS 上的資料傳輸其實已經是加密過的了。
有些人會說既然 HTTPS 已經把傳輸資料加密了,那密碼是否就可以直接明文傳輸? 這個沒有一定的答案,但我自己是覺得不手動做點加密心裡感覺怪怪的,而且 SSL 憑證在網路上有免費的也有付費的,我自己會直接用免費的,但就覺得是不是不夠安全,所以還是會再做下一步的不對稱加密。
順帶一提整個 HTTPS 的請求流程簡單來說就是在做不對稱加密,也是透過公鑰私鑰來做身分驗證,所以才會有人說幹嘛 HTTPS 做一次加密,你又自己做一次加密呢?對 HTTPS 流程有興趣的人可以去看看這篇文章的第一段。
對稱加密
好的,來到加密的步驟了,加密可以分為「對稱加密」和「非對稱加密」。
對稱加密就是指用同一把 key 做加密和解密的動作,那我們也知道所有前端的程式都是公開在網路上的,所以把 key 直接放在前端是不行的,如果是透過網路從後端傳送給前端,也一樣會有在傳輸過程中被中間人擷取的風險。常見的對稱加密演算法有 AES 和 DES。
非對稱加密
相較於對稱加密是用同一把 key 在做加解密,非對稱加密則是有分公鑰和私鑰,透過公鑰加密的資料,只能透過對應的私鑰才可以解密。
在登入的流程中,大致上就是在登入前會先有一隻 API 是前端請求公鑰的,接著前端將密碼用公鑰加密,透過登入 API 將加密後的資料傳送到後端,後端接到資料後就可以透過私鑰解密了。常見的非對稱加密演算法有 RSA。
請求 public key
登入時帶的資料(例如帳號密碼)是加密後的資料
把密碼儲存進資料庫中
編碼
首先是編碼,例如常見的 Base64,雖然經過編碼後對人類來說勉強算是亂碼,但其實看字尾的=
或==
就很容易猜到這是 Base64,接著丟到解碼網站就可以輕易得到密碼了喔。
就算改成用少見的編碼算法,對於駭客來說大概還是跟明文儲存沒什麼差別吧,所以不要用編碼來儲存密碼。
加密
跟前面講得一樣分為對稱加密和非對稱加密,對稱加密的話,你的 key 總是要存在某個地方,不是資料庫就是網站後端,對於能看到資料庫的駭客或內部工程師來說,其實拿到 key 也不會有什麼困難。
那非對稱加密呢?先想想你的每個經過公鑰加密的密碼,都有一個對應的私鑰,既然是對應的私鑰,那當然只能和加密後的密碼一起存在資料庫阿,那這樣又有什麼意義呢?所以加密也不行。
Hash 雜湊
雜湊是不可逆的,因此若資料庫儲存的是密碼的雜湊值,那即使資料庫被駭了也沒辦法回推原本的密碼,而要驗證使用者輸入的密碼則只要把輸入的密碼再進行一次雜湊,看看得到的雜湊值是否和資料庫內儲存的相同,就可判斷密碼是否正確了。
但要注意的是,雜湊函數是有機率碰撞的,因此可能不同的密碼卻得到同樣的雜湊值,因此碰撞機率也是挑選雜湊函數的重要考慮因素。
雜湊函數的選擇
所以到底要怎麼挑選雜湊函數呢,有三個方向可以考慮
- 安全性 ( md-5, sha-1 已被破解 )
- 碰撞機率 (基本上你聽過的、大家常用的都不容易碰撞,但還是有小機率會發生,不過你自己寫的可能是大機率碰撞 )
- 運算速度 ( 選運算慢的可避免被彩虹表反推 )
彩虹表
指把所有文字的排列組合丟進 hash function 並算出結果,並儲存而成的一張表,用於將 hash 值反推原始字串。例如網路上隨便找都能找到 md-5和 sha-1 的對照表網站,把 hash 值丟進去馬上查給你原始字串。
若 hash function 計算速度很快,彩虹表就越容易被產出,因此選擇計算較慢的 function 可避免被輕易用彩虹表反推。
加鹽雜湊
除了選一個好的 hash function 以外,我們還能加鹽。
加鹽是指將密碼加上一串複雜特殊符號例如h7.@-]%<#L
,變成HiLisa.h7.@-]%<#L
後再丟進去雜湊函數,因為原文夠長也夠亂,所以不容易被網路上的表暴力破解。
推薦雜湊函數
- bcrypt ( 運算慢,也是目前流行的雜湊算法 )
- Argon2 ( 熱心人士補充,好像比 bcrypt 更新也更好 )
總結
- 傳輸密碼:用 HTTPS + 非對稱加密 (e.g. RSA)
- 儲存密碼:儲存密碼的雜湊值 (e.g. bcrypt, Argon2)
- 驗證密碼:把輸入密碼雜湊後,和資料庫中的值比對,相同即表示極大機率是正確密碼