作為一名有多年經驗的前端,在剛開始學習web后端的時候,就對如何設計一個安全的 web 系統用戶密碼管理流程有很多疑問。之前自己也實踐過幾種方法,但一直覺得不是十分安全。
我們知道,用戶在注冊或登錄界面填寫的密碼是明文的,我們需要將密碼安全的傳輸到后端,保存到數據庫,或者完成登錄校驗。
我一開始擔心的是在網絡傳輸中造成密碼的泄露,所以確定了以下方案,1. 使用 https; 2. 使用 rsa 公鑰加密。具體流程如下:
注冊流程:前端獲取公鑰 -> 前端用公鑰加密明文密碼 -> https 傳輸給后端 -> 后端私鑰解密(確保密文是可以解密的) -> 存入密文至數據庫
登錄流程:前端獲取公鑰 -> 前端用公鑰加密明文密碼 -> https 傳輸給后端 -> 后端私鑰解密,讀取數據庫密文并解密 -> 對比密碼是否一致
在這樣設計了以后,很快被后端同學打臉。你在私鑰被竊且拖庫的情況下,所有用戶的明文密碼都會被解密出來。
很快,我經過思考后,調整了流程
注冊流程:前端獲取公鑰 -> 前端用公鑰加密明文密碼 -> https 傳輸給后端 -> 后端私鑰解密得到明文密碼 -> 生成隨機鹽 -> 明文密碼+鹽并計算 hash 值 -> 存入 hash 值至數據庫
登錄流程:前端獲取公鑰 -> 前端用公鑰加密明文密碼 -> https 傳輸給后端 -> 后端私鑰解密得到明文密碼 -> 讀取數據庫用戶信息得到該用戶鹽值 -> 明文密碼+鹽并計算 hash 值 -> 對比計算出來的 hash 值和數據庫中的 hash 值是否一致
經過這樣的設計以后,數據庫中就沒有任何用戶明文密碼了。即便私鑰被竊且被拖庫,所有用戶的明文密碼都不會泄露。
我自己開發的某個系統,用這樣的設計跑了很久,也沒有遇到什么問題。但是最近我再思考這個問題的時候發現,這樣還是不安全。
假設,服務器的 root 權限被攻破,黑客可以通過修改程序,直接打印私鑰解密明文密碼的結果,這樣,用戶的明文密碼還是會被泄露。
怎么辦?我再想了很久,發現,只要增加一個流程,即可避免這個問題。
注冊流程:前端獲取公鑰 -> 前端將明文密碼取 hash 值 -> 前端用公鑰加密 hash 值 -> https 傳輸給后端 -> 后端私鑰解密得到前端傳的 hash 值 -> 生成隨機鹽 -> hash 值+鹽并計算二次 hash 值 -> 存入 二次hash 值至數據庫
登錄流程:前端獲取公鑰 -> 前端將明文密碼取 hash 值 -> 前端用公鑰加密 hash 值 -> https 傳輸給后端 -> 后端私鑰解密得到前端傳的 hash 值 -> 讀取數據庫用戶信息得到該用戶鹽值 -> hash 值 +鹽并計算 hash 值 -> 對比計算出來的 hash 值和數據庫中的 hash 值是否一致
通過這樣的設計,明文密碼只存在用戶前端填寫的表單上,在傳輸過程中,以及程序運行和數據存儲中,都是沒有明文密碼的。也就是說,即便私鑰被竊,數據庫被拖庫,服務器 root 權限被攻陷,整個系統崩潰,用戶的明文密碼依然被保護二不會泄露。
我將我的設計扔給 deepseek,它表示這樣的設計已經是非常高等級的安全了。同時,它表示,如果要實現金融級別的安全,還需要做如下完善:
- 給每個請求分配一個隨機鹽值,讓前端用明文密碼加隨機鹽值計算 hash ,確保每次用戶傳輸的數據中的 hash 值是不一樣的,防止被彩紅攻擊。
- 對接口請求進行頻次限制,防治被暴力破解。
我覺得除非是真的開發金融及的應用,否則上面兩點增加的復雜度不值得一般的小的系統。所以,我就不打算在我開發的這個小的工具系統里采用這樣高等級的措施了。
那么各位看官,您覺得還有沒有更好的實現方式呢?
一位多年后端開發經驗的老哥給我講,你想得太TM復雜了,絕大多數系統都是 https + 明文密碼傳輸的,后端弄個鹽在數據庫存個 hash 就算很注重安全了……我聽后大為震驚……