Symmetric

這種對稱密鑰 MAC-then-encrypt 身份驗證令牌方法是否安全?

  • December 20, 2016

**注意:**這個問題最初將“數字簽名”和“MAC”混為一談,我從那時起就知道這不是一回事。對“簽名”的任何引用(在答案或任何評論中)都應理解為“MAC”。

我正在嘗試使用身份驗證令牌,並且正在嘗試圍繞最佳方法進行思考。

目標

  1. 成功登錄後,將創建一個包含 users accountID、 anonce和 an的令牌expiry。然後將令牌傳遞回客戶端以用於下一個請求。
  2. 客戶端將令牌與每個請求一起傳遞回伺服器,以辨識accountID任何給定請求的來源 ()。
  3. 令牌包含隨每個請求更改的隨機數。
  4. AMAC是根據plaintext值創建的,以確保它不被篡改。
  5. 然後plaintext對令牌的值(包括MAC)進行加密,以確保它被安全地隱藏。
  6. 客戶從不知道macKeyencryptionKey. 客戶端不打算訪問plaintext令牌的內容。

目前方法

///process.env.TOKEN_ENCRYPTION_PASSWORD = '13sd4089f-268c-483d-9e82-jk3c1b47c77a';
///process.env.TOKEN_MAC_KEY = '1fde05f4-268c-483d-9e82-85fc1b42321';

/// Successful login by user ...

var token = {
   nonce : 'SOME-UUID-GOES-HERE',
   accountID : 'account-12345',
   expires : 1234567890
};

token.mac = crypto.createHmac('sha512', TOKEN_MAC_KEY)
                 .update(JSON.stringify(token))
                 .digest('base64');

var encryptedToken = cryptoJS.AES.encrypt(JSON.stringify(token), TOKEN_ENCRYPTION_PASSWORD).toString();

response.send({
   message : 'Created, MAC'd, and encrypted an auth token.',
   authToken : encryptedToken
});

問題

  1. 在這種情況下,採用一種MAC-then-encrypt方法是否安全?我已經閱讀了一些關於此的意見,並且比我開始時更加困惑。
  2. 我已經閱讀了cbc-mode在使用方法時應該使用的內容MAC-then-encrypt。這是預設模式CryptoJS.AES.encrypt()嗎?
  3. CryptoJS.AES看起來很一般。這是預設的AES256嗎?或者那是我需要在我的程式碼中明確聲明的東西?
  4. 將字元串傳遞給CryptoJS.AES.encrypt()我時,我讀到它會自動生成一個key和和iv“幕後”。key生成我自己的and會更安全iv,還是應該讓CryptoJS處理它?
  5. 使用一種MAC-encrypt-MAC方法是否可以獲得更多的安全性,或者這只是增加了我的應用程序邏輯的複雜性?
  1. 在這種情況下,使用先簽名後加密方法是否安全?我已經閱讀了一些關於此的意見,並且比我開始時更加困惑。

首先,鑑於您的範常式式碼使用的是 HMAC,而不是數字簽名,我假設您的意思是 MAC-then-encrypt。

結合 MAC 和加密的一般安全方法是先加密後 MAC:首先加密消息,然後在密文上計算 MAC(包括 IV 和您可能擁有的任何相關數據!)。解密時,首先驗證MAC,然後才嘗試解密消息。

Encrypt-then-MAC 是安全的,因為它保證 MAC 和密碼各自只需要做自己的工作:加密時,MAC 只接收已經加密的輸入,因此不會意外洩露明文資訊;解密時,密碼只接收已經被 MAC 認證的輸入,因此它不需要擔心安全地處理偽造或修改的消息。

但是,如果 MAC 和密碼選擇得當,MAC-then-encrypt(甚至 MAC-and-encrypt)*可能是安全的。*特別是,MAC-then-encrypt 安全性的充分條件是密碼模式必須是保長的——即密文(不包括 IV)必須與輸入明文完全一樣長。

CFB、OFB 和 CTR 模式都是保留長度的,但 CBC 不是(除非結合稱為“密文竊取”的修改)。特別是,因為正常的 CBC 模式需要將消息填充到整數個密碼塊中,所以它通常容易受到填充預言攻擊,除非在加密應用 MAC 進行保護(因此在解密之前進行驗證)。因此,您不應將 CBC 模式與 MAC-then-encrypt 一起使用。

  1. 我讀過在採用先簽名後加密方法時應該使用 cbc-mode。這是 CryptoJS.AES.encrypt() 的預設模式嗎?

這很難說,可能取決於您使用的 CryptoJS 版本。 這個頁面說預設是OFB模式。 這個頁面(對於 CryptoJS v3)說它是 CFB。這兩個似乎都是非官方的第三方來源,但我無法找到該庫的任何全面的官方文件。顯然它“仍然有點稀疏”。:(

在任何情況下,如上所述,您都不應該CBC 模式與 MAC-then-encrypt 一起使用。如果要使用 CBC,請在加密後應用 MAC。

  1. CryptoJS.AES 看起來很通用。這是預設為 AES256 嗎?或者那是我需要在我的程式碼中明確聲明的東西?

請參閱上一個答案。根據連結的 Google Groups 文章(當然,這可能可靠也可能不可靠),該庫可以處理 AES-128、AES-192 或 AES-256,大概取決於您傳入的密鑰的大小。如果您只輸入一個密碼,我不知道它會選擇哪個 AES 變體。

(我還想藉此機會重申我不喜歡記錄不充分的加密庫。 如果沒有人知道它應該做什麼以及如何正確和安全地使用它,那麼你的庫編碼得有多好並不重要.)

  1. 在將字元串傳遞給 CryptoJS.AES.encrypt() 時,我讀到它會自動生成一個密鑰,並且 iv ‘在幕後’。生成我自己的密鑰和 iv 會更安全,還是應該讓 CryptoJS 處理?

看起來如果您將encrypt()純字元串作為密鑰傳遞給該方法,它假定該字元串是使用者提供的密碼,並在其上應用基於密碼的密鑰派生函式(如 PBKDF2)。此類功能被故意設計得很慢,以防止暴力破解密碼。因此,只要有可能,您自己只呼叫一次密鑰派生函式並儲存它輸出的原始 AES 和/或 HMAC 密鑰會更有效。

您幾乎可以肯定沒有任何理由提供自己的靜脈注射;任何體面的加密庫都應該能夠為您生成非常好的隨機 IV。

  1. 使用簽名加密簽名方法是否可以獲得更多安全性,或者這只是增加了我的應用程序邏輯的複雜性?

一般來說,用 MAC-encrypt-MAC 代替普通的 encrypt-then-MAC 是沒有意義的。也就是說,它也不應該受到傷害(除了浪費幾個 CPU 週期,並稍微增加你的密文長度),它可能會保護你免受一些實現錯誤(比如忘記在外部 MAC 輸入中包含 IV)。

但是,如果可能的話,您真正應該做的是使用集成的身份驗證加密模式,如 SIV(我目前個人最喜歡的)或 GCM(甚至是結合了它們的新 GCM-SIV 模式)。這些加密模式帶有內置的消息身份驗證,並負責安全、正確地結合加密和身份驗證。

(對於實際的數字簽名,sign-encrypt-sign 有時可能很有用,因為雙重簽名可以防止某些攻擊,例如攻擊者獲取有效的加密然後簽名的消息,剝離簽名並替換他們自己的簽名,或者惡意接收者解密簽名然後加密的消息並將其重新發送給其他人,保留原始簽名。但是,在大多數情況下,有更有效的方法可以避免此類攻擊。有關更多詳細資訊,請參見例如這個早期的問題。)

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