Aes

這個用於屏蔽 DB id 的協議是否合理?

  • April 29, 2019

問題陳述

我不想公開我的內部數據庫 id(或其他內部私有 id),以便更難濫用它們,例如猜測序列的下一個 id 或類似的。為我的數據庫中的每個條目添加一個具有公共、不可猜測的 UUID 的新欄位對我來說是不可行的。因此,我需要一個可以(可逆地)掩蓋我的身份的協議。

所需屬性

  • 序列中的 ID 在被屏蔽時應該難以猜測和辨識
  • 偽裝時,ID 應該很難偽造
  • 單個遮罩 id 的妥協不應使包含其他遮罩 id 變得更容易
  • 屏蔽相同的 ID 應該會隨機產生不同的輸出
  • 遮罩 ID 不應太長
  • 掩蔽應該相當快

目前最先進的

有一個流行的庫試圖解決這個問題:https ://hashids.org/這裡有一個 Java 實現。HashIds 基本上使用特定字母對 id 進行編碼,該字母取決於呼叫者提供的“鹽”。此外,他們嘗試省略某些字母,以便將生成帶有英語詛咒詞的 id 的機會降到最低。ID是完全確定的,即。相同的數字和相同的鹽會產生相同的輸出。

新的密碼學方法

我發現所描述的問題很有趣,但對實際上掩蓋 id 的悲慘安全屬性感到失望(公平地說,HashId 從不聲稱不僅僅是混淆)。我的想法可能類似於key wrapping的專用版本。

所以我的方法是:

id         .... 8 byte number
entropy    .... 4, 8, 12, 16 byte random value
secret-key .... 16 byte high quality random key assumed to be stored in a safe manner

Primitives:
AES-CTR(iv, key)
HMAC_SHA256(key, data)
HKDF-Expand(PRK, info, L) -> OKM
  1. 為每個id創建一個entropy4 到 16 個字節長的隨機字節數組。
  2. 導出 64 字節密鑰km材料HKDF-Expand(secret-key, entropy, 64)
  3. 將 64 個字節拆分為:
  • roundSecretKey=km[0-16]
  • iv=km[16-32]
  • macKey=km[32-64]
  1. idAES-CTR(iv, roundSecretKey)->加密encryptedId
  2. HMAC_SHA256(macKey, encryptedId | iv )使用->創建 machmac
  3. 將 mac 截斷為 4-16 字節 ->truncated_hmac
  4. 使用以下命令創建輸出消息: masked_id = entropy | encryptedId | truncated_hmac

討論

選擇 AES-CTR 作為加密原語是因為:

  • 基本上每種程式語言都有它的標準實現
  • 是一種流密碼,可以有效地加密小於 16 字節的數據
  • AES 可以相當快(並且通常是硬體加速的)

然而,最大的缺點是密鑰 k1 的 AES-CTR 絕不能與 iv1 一起使用超過一次。

HMAC 用於為生成的 id 添加完整性和真實性,以防止偽造。

隨機entropy部分確保id1多次屏蔽很可能會創建不同的屏蔽 id。

妥協

保持被屏蔽的 id 短,entropy並且hmac保持短(或非常短取決於配置)。假設最壞的情況:4 字節熵​​。重複靜脈注射的機會是現實的,但很少。攻擊者不應該能夠解密,因為ivroundSecretKey依賴於secret-key需要首先暴力破解的秘密。一個選項是切換到 AES-CBC,但這會使最小輸出長度為 16 字節(大多數 id 為 8 或 16 字節)。

保持 hmac 小可以更容易偽造消息,但它應該更容易獲取macKey.

這兩個問題都可以通過將entropy和的長度hmac增加到 16 字節來解決。然而,這會將輸出長度增加到 40 字節。


**所以社區的問題是:這個協議是否符合所需的屬性?妥協合理嗎?這基本上是一個健全的協議嗎?**我正在尋找一般回饋、安全問題、可能的改進 - 目前我不是在尋找解決公共 ID 問題的不同方法。


感興趣的讀者更新:這裡是Github 上 id 加密模式的參考實現,這裡有一篇關於它的文章。

您正在尋找的是 64 位長的短位字元串的隨機驗證密碼。您似乎願意接受 64 到 256 位的密文擴展。

  • 帶著 $ r $ -bit 隨機化字元串,之後發生碰撞的機率 $ n $ 密文——至少會揭示遮罩 ID 的相等性——充其量是約 $ n^2/2^r $ .
  • 和 $ t $ -bit認證擴展,偽造後的機率 $ f $ 嘗試最多以大約為界 $ f/2^t $ .

您可以使用任何您喜歡的隨機驗證密碼,例如NaCl crypto_secretbox_xsalsa20poly1305,它具有 192 位隨機數和 128 位驗證標籤;如果您使用隨機統一選擇的 128 位隨機數(用零填充到 192 位),只要您將自己限制在遠低於 $ 2^{64} $ id,例如將自己限制為十億個 id。您可以使用 AES-GCM,但限制更小,因為 nonce 是 96 位,因此您需要將自己限制在遠低於 $ 2^{48} $ 身份證。當然,這裡的 nonce 碰撞是災難性的。

如果你想用 AES 和 SHA-256 建構它,你可以使用 AES-CBC 和 encrypt-then-MAC 和 HMAC-SHA256 的 128 位截斷。由於輸入是固定長度的,因此不需要填充;實際上,它只是$$ E_{k_1,k_2}(\rho, m) := c \mathbin| \operatorname{HMAC-SHA256-128}{k_2}(c), \quad \text{where} \quad c = \rho \mathbin| \operatorname{AES}{k_1}(\rho \oplus m). $$ 在發生碰撞的情況下 $ \rho $ ,這仍然只洩漏遮罩 id 的相等性。顯然,你可以得出 $ k_1 $ 和 $ k_2 $ 從單個 32 字節主密鑰 $ k $ 使用 HKDF-SHA256。

這是一個非常簡單的方法,它使用單個密鑰對 AES 的單個呼叫來加密 64 位 id $ m $ 使用 64 位隨機化 $ \rho $ 下鍵 $ k $ :$$ E_k(\rho, m) := \rho \mathbin| \operatorname{AES}k(\rho \mathbin| m). $$ 這裡 $ r = t = 64 $ ,所以我們使用 192 位來加密 64 位 id。讓 $ \pi $ 是均勻隨機排列;對於任何偽造企圖 $ \rho_1 \mathbin| c_1, \dots, \rho_f \mathbin| c_f $ , 存在的機率 $ i $ 這樣 $ \pi^{-1}{64}(c_i) = \rho_i $ 由 $ f/2^{64} $ , 在哪裡 $ \pi^{-1}_{64}(c_i) $ 是前 64 位 $ \pi^{-1}(c_i) $ . 因此,任何算法的區分和偽造優勢 $ E_k $ 由 $ (n^2 + f)/2^{64} + \varepsilon $ 在哪裡 $ \varepsilon $ 是均勻隨機排列在 AES 中的最大優勢。如果你屏蔽一百萬個 ID,並且你的頻寬將對手限制在一萬億次偽造嘗試,那麼對手獲勝的機率不到八千分之一。即使發生碰撞 $ \rho $ ,同樣這只會洩露遮罩 ID 的相等性,儘管偽造可能是一個更大的問題。

你描述的方案呢?它比必要的更複雜:例如,您可以取消 IV,而始終使用零。如果您按照您的建議使用 32 位隨機化字元串,那麼您將在幾萬個 id 之後看到高機率的衝突,當這種情況發生時,這不僅會洩漏兩個遮罩 id 的相等性,而且兩個遮罩 ID 的異或。

引用自:https://crypto.stackexchange.com/questions/68415