如果我只有另一個雜湊,我如何驗證雜湊密碼?
情景
我有一個客戶端 Web 應用程序,它針對伺服器端 API 反彈請求。為簡單起見,每個請求都必須傳遞使用者名和密碼。這類似於老式的 XML-RPC,其中使用者名和密碼在每個請求中作為參數傳遞。
但是,由於這是一個 REST 服務,一些請求(GET 和 DELETE)沒有消息正文,並且必須將使用者名和密碼作為 URL 中的查詢變數傳遞:
GET http://application.url/endpoint?username=xxxx&password=xxxx
坦率地說,這是一個壞主意。請求記錄在伺服器上,因此如果我以明文形式發送密碼,它現在位於伺服器某處的日誌文件中。我不想通過網路以明文形式發送密碼,所以我陷入了兩難境地。
方法一
一位同事提出了解決這個無線問題的第一種方法:
- 使用諸如 Blowfish 之類的東西加密密碼並將其儲存在數據庫中。
- 客戶端使用一些已知的方案(即SHA512)對明文密碼加上一些鹽進行散列。
- 客戶端將雜湊和鹽發送到伺服器。
- 伺服器解密儲存的密碼,用提供的鹽對其進行散列,並將其與提供的散列進行比較。
這是一種通過網路發送敏感數據的有效方法(如果讀取了日誌,則沒有人能夠獲取密碼)。但它違反了我的另一條個人安全規則——我可以訪問使用者的原始密碼!
如果密碼使用可逆加密儲存在數據庫中,那麼它仍然容易受到攻擊。有人可以竊取我的數據庫並解密密碼。對我來說,這幾乎和一開始就以明文形式儲存一樣糟糕。
伺服器不應訪問純文字密碼。
方法二
這種方法是由一位開發人員順便提出的,從表面上看,似乎解決了我的一些問題。
- 用一些鹽對密碼進行散列,並將散列和鹽儲存在數據庫中(沒有明文!)。
- 客戶端使用伺服器已知的公共 RSA 密鑰加密密碼
- 客戶端將密文發送給伺服器。
- 伺服器使用其私鑰解密密碼,並使用已知的鹽對恢復的明文進行雜湊處理。
- 伺服器將計算的雜湊值與儲存在數據庫中的雜湊值進行比較。
再次,一種防止通過網路發送明文密碼的有效方法。它還有一個額外的好處,那就是永遠不會將明文儲存在伺服器上。
但這仍然困擾著我,因為伺服器可以在解密後看到明文。儘管我今天可以建構安全的系統,但不能保證其他開發人員將來不會在某處添加日誌記錄並將明文寫入磁碟。
**如果伺服器永遠無法訪問純文字密碼,**我會更高興。
最終目標
目前,方法#2 是我提出的最佳解決方案,並且可能是我最終實施的解決方案。但是那裡有一個理想的解決方案……我只是不知道它可能有多實用(甚至可能):
- 用一些鹽對密碼進行雜湊處理並將兩者都儲存在數據庫中(沒有明文!)。
- 客戶端使用自己的鹽對密碼進行雜湊處理。
- 客戶端將雜湊(和鹽?)發送到伺服器。
- 伺服器執行一些其他功能來查看兩個雜湊(它的和客戶端的)是否由相同的明文生成 - 它沒有得到明文。
我可以連接某種質詢/響應系統(客戶端要求加鹽,用該鹽對密碼進行雜湊處理,發送響應,伺服器將其與預期的內容進行比較),但我試圖保留請求到最低限度。因此,當您發出請求時,您只需呼叫一次伺服器,而不是呼叫一次進行身份驗證,再呼叫一次來執行。
最後一種情況是否可能?
使用下面的方法,伺服器永遠不會看到密碼或密碼解密的密鑰。
要生成密碼:
客戶端生成一個新的 RSA 密鑰,用密碼對其進行加密(使用類似 PBKDF2 的東西來生成對稱密鑰),然後將 RSA 公鑰和加密的私鑰交給伺服器。
驗證:
伺服器向客戶端發送加密的 RSA 私鑰和質詢。客戶端使用密碼解密 RSA 私鑰,然後對質詢進行簽名。客戶端將簽名的質詢發送回伺服器。伺服器使用 RSA 公鑰驗證簽名。
警告:
確保更改密碼涉及創建新私鑰,而不是使用新密碼重新加密舊私鑰。
–
這有一個額外的要求。您可以通過兩個技巧來避免這種情況:
- 讓客戶端儲存加密的私鑰,這樣它就不需要向伺服器詢問它。
- 讓客戶端儲存一個序列號以用作質詢(或時間戳和隨機數),因此它不需要從伺服器獲取質詢。