在數據庫中儲存消息的端到端加密網路聊天應用程序
我正在開發具有集成聊天功能的網路應用程序。所有聊天只能在兩個使用者之間進行 - 不多也不少。
我非常關心安全性,所以我想用端到端加密來加密所有聊天消息。到目前為止,我最喜歡的協議是 Signal 協議。
但是,網路應用程序的問題是(據我所知)不可能可靠地儲存使用者的私鑰。此外,您不能真正在本地保存任何內容(即在瀏覽器的本地儲存中),因為可以從任何地方訪問 Web 應用程序,並且使用者應該能夠在無論身在何處登錄後立即查看所有消息。
所以我的解決方案是:
- 使用者 X 在他的瀏覽器中創建一個帳戶。
- 創建一個密鑰對,並使用輸入的密碼對私鑰進行對稱加密。
- 公鑰和加密的私鑰被發送到儲存在數據庫中的伺服器。
然後,如果使用者 Y 想與 X 開始聊天:
- Y 的消息在他的瀏覽器中被加密了兩次:
- 一旦使用儲存在數據庫中的使用者 X 的公鑰,
- 然後使用使用者 Y 的密碼。
- 兩種加密版本的消息都通過 WebSocket 傳輸到伺服器。
- 如果使用者 X 目前已登錄,伺服器會將消息的第一個版本發送給使用者 X。此外,它會將消息的兩個版本都儲存在數據庫中。
- 使用者 X 的瀏覽器在他登錄後立即解密從伺服器接收到的消息並將它們顯示給使用者。
每次使用者 X 登錄並查看聊天時:
- 使用者登錄並收到加密的私鑰。使用輸入的密碼,私鑰被解密。
- 從伺服器請求已經存在的聊天消息。
- 在本地,消息使用私鑰解密。
這完全安全嗎?
我認為這可以防止在研究其他方法時出現以下一些缺點:
- 當使用者更改密碼時,伺服器不必對所有消息進行解密和重新加密。
- 每次使用者在新位置登錄時,瀏覽器都不需要生成新的密鑰對。
- 消息可以長期儲存,以便兩個使用者輕鬆閱讀。
我想到的一個潛在漏洞是:
- 由於數據庫中儲存了兩個版本的消息,潛在的攻擊者可以更輕鬆地暴力破解原始內容。不過,這可以通過使用一些特定於使用者的值(即使用者名)對兩條消息進行簽名來防止。
只需使用使用者密碼對所有與使用者相關的資訊進行加密即可。確保使用安全的基於密碼的密鑰派生函式(PBKDF2 似乎浮現在腦海,但我個人更喜歡 Argon2),因為密碼會產生糟糕的加密密鑰。這是如何完成的範例:
- 使用者在客戶端生成隨機加密密鑰(主密鑰或 MK)(不是基於密碼,實際上是隨機的)
- 使用者從他們的密碼生成一個密鑰加密密鑰 (KEK)
- 使用者使用密鑰 KEK 加密 MK
- 使用者使用 MK 在他們的瀏覽器中加密他們的數據。
這樣,如果使用者要更改密碼,他們可以簡單地使用新的 KEK 重新加密 MK。或者,如果他們認為他們的 MK 已被破壞,他們可以選擇更改他們的 MK*和KEK。*您不再需要任何東西,因為加密完成了工作。其他一切都可以像客戶端儲存可用一樣完成,因為它受到 MK 的保護。
這聽起來很像您正在做的事情,但聽起來不像您正在使用 KEK 或密鑰派生函式,如果系統要安全和可擴展,這兩者都應該是這種情況。
創建一個密鑰對,並使用輸入的密碼對私鑰進行對稱加密。
使用 MK 加密,而不是密碼
Y 的消息在他的瀏覽器中被加密了兩次
由於數據庫中儲存了兩個版本的消息,潛在的攻擊者可以更輕鬆地暴力破解原始內容。不過,這可以通過使用一些特定於使用者的值(即使用者名)對兩條消息進行簽名來防止
這不會影響安全性,假設你做對了。每條消息都應該使用隨機 IV加密才能正常工作。確保它是隨機的,並且對於每條消息都是唯一的。*這將使您需要加密的資訊量增加一倍,但其他任何事情都是不安全的。*伺服器可以知道 IV,而不會影響安全性,因此不需要加密。
在瀏覽器中保留“原始”密碼作為對稱加密/解密的密鑰可能是一個問題。
這是對的。絕對從密碼中獲取密鑰。
密碼長度有限,如果直接用作對稱密鑰,可能無法提供高強度的密碼。一種選擇可能是從中派生密鑰
一定要這樣做!使用專門設計用於從密碼中獲取加密密鑰的函式。
var key = crypto.createCipher(‘aes-128-cbc’, ‘秘密密碼’);
這是一個壞主意(對不起,但確實如此)。AES 不是密鑰派生函式。*請不要使用 AES 派生密鑰!*使用 PBKDF2 或 Argon2;它們是專門為此目的而製造的。
這種方法不允許對聊天消息進行伺服器端搜尋。客戶端搜尋可以在載入到 UI 組件中的有限文本上實現。
那是對的。但是,如果我是使用該服務的人,我會期望的;我會自動不信任任何允許伺服器端搜尋的東西。
當使用者更改密碼時,伺服器不必對所有消息進行解密和重新加密
只有使用我描述的 MK-KEK 方案才能做到這一點。我還應該補充一點,客戶端應該進行加密和重新加密,而不是伺服器。
更改密碼意味著您至少必須重新加密某些內容。此外,如果 MK 遭到破壞,您仍然必須重新加密所有內容。做好準備,或者冒著犧牲安全的風險(這對我來說是不可原諒的罪過)。