2014-04-19 33 views
3

我想學習使用哈希和鹽漬保存數據庫密碼的密碼學,所以我決定做一個登錄系統嘗試實現這個系統。PBKDF2鹽醃和散列

我的數據庫由

  • 用戶ID INT PK
  • 用戶名VARCHAR(250)
  • VARBINARY(64)
  • 密碼VARBINARY(64)
  • RegDate 日期時間
  • 電子郵件爲varchar(250)

我使用PBKDF2,但似乎這不是一個散列/鹽析法,它是什麼,如果它是不?

如果是的話我是這麼做的嗎?

我的鑰匙

private const int SALT_SIZE = 64; 
private const int KEY_SIZE = 64; 

插入數據到數據庫

public static void RegisterMe(string _username, string _password, string _email) 
     { 
      using (var cn = new SqlConnection(User.strcon)) 
      { 
       string _sqlins = @" 
        INSERT INTO 
        [User] 
         ([Username],[Salt],[Password],[RegDate], [Email]) 
        VALUES 
         (@Username, @Salt, @Password, CURRENT_TIMESTAMP, @Email)"; 

       var cmd = new SqlCommand(_sqlins, cn); 
       cn.Open(); 
       using (var deriveBytes = new Rfc2898DeriveBytes(_password, SALT_SIZE)) 
       { 
        byte[] salt = deriveBytes.Salt; 
        byte[] key = deriveBytes.GetBytes(KEY_SIZE); 

        // save salt and key to database 
        cmd.Parameters.AddWithValue("@Username", _username); 
        cmd.Parameters.AddWithValue("@Password", key); 
        cmd.Parameters.AddWithValue("@Salt", salt); 
        cmd.Parameters.AddWithValue("@Email", _email); 
       } 
       cmd.ExecuteNonQuery(); 
      } 
     } 

檢查,如果用戶是有效

public bool IsValid(string _email, string _password) 
    { 

     using (var cn = new SqlConnection(strcon)) 
     { 
      byte[] salt = { }, key = { }; 
      string _sql = @" 
          SELECT 
           SALT, 
           [Password], 
           UserID 
          FROM 
           [User] 
          WHERE [Email] = @email"; 

      SqlCommand cmd = new SqlCommand(_sql, cn); 
      cmd.Parameters.AddWithValue("@email", _email); 

      cn.Open(); 
      SqlDataReader reader = cmd.ExecuteReader(); 
      if (reader.Read()) 
      { 
       salt = reader.GetSqlBytes(0).Value; 
       key = reader.GetSqlBytes(1).Value; 

       reader.Dispose(); 
       cmd.Dispose(); 
       using (var deriveBytes = new Rfc2898DeriveBytes(_password, salt)) 
       { 
        byte[] newKey = deriveBytes.GetBytes(KEY_SIZE); // derive a 20-byte key 
        return newKey.SequenceEqual(key); 
       } 
      } 
      else 
      { 
       reader.Dispose(); 
       cmd.Dispose(); 
       return false; 
      } 
     } 
    } 

我的系統工作正常,它以字節爲單位將數據設置到數據庫中,如果用戶輸入正確的密碼,則返回true。但這是正確的方式嗎?這是甚至哈希/醃製?

+0

我不是很熟悉使用PBKDF2,但從它的外觀我認爲*你是正確的,你實際上沒有哈希密碼。嘗試閱讀此MSDN文檔(http://msdn.microsoft.com/zh-cn/library/system.security.cryptography.rfc2898derivebytes.aspx),其中顯示了一些示例用法,但是這是使用對稱加密。散列是不對稱的,所以你應該永遠不能在原始明文被散列後得到它。 –

+0

@martin_costello我認爲它是哈希密碼。它不使用對稱加密PBKDF2基於HMAC,而不是密碼。散列不是不對稱的,但它確實是一種方式。我非常熟悉PBKDF2。 –

+0

@owlstead我的意思是說MSDN的例子是對稱的。 –

回答

8

你基本上要在正確的方向,但我會指出一些事情要考慮:

  1. 的PBKDF2方法的迭代的默認數量可能不夠,你可能不想把事情留給默認。我會建議指定一個至少10K的迭代計數。

  2. 在另一方面,密鑰大小和鹽大小是通過在字節此實現計數。 64字節有點太多了。保持兩個16字節每個應該是充足的。不建議超過20個字節,因爲它是底層散列函數/ HMAC的最大大小。這樣做只會給攻擊者帶來好處(根據很多人的看法,這是PBKDF2的一個設計錯誤)。您當然可以將varbinary的大小設置爲更高的值以允許將來的升級。

  3. 我們建議您保留協議號與鹽散列密碼。這樣做可以讓您在以後的日期和每次登錄時升級計劃,以便用戶可以重置他/她的密碼。

  4. 小點;當產生鹽時,MSDN不指定。我會檢查鹽的隨機性(檢查它是否每次都不相同),並且在調用getBytes後詢問鹽以確保鹽確實是隨機的,即使實施方式發生變化。否則使用密碼安全的隨機數生成器自己生成它。

+0

非常感謝!這對我幫助很大! 1:通過閱讀你的評論和微軟文檔,Rfc2898DeriveBytes(String,Int32,Int32)是迭代的​​第三個參數嗎?所以你會推薦使用10K int?怎麼來的? 2:得到你,我想,爲什麼不把它變得更長,因此更難打破。但我可以清楚地看到爲什麼這也是一個缺點! 3:你的協議號碼是什麼意思? 4:雅我也要實現這一點,但在所有其他步驟之前沒有考慮它,並且我的哈希/鹹文寫得正確! :) – Sigils

+0

是的,第三個int是迭代計數。 1K在開始時被指定,但被認爲是低的。 10K是實際使用中最低的,但請檢查[此問題](http://stackoverflow.com/q/6054082/589259)。迭代計數有助於防止攻擊者暴力破解密碼。它幫助更多的確保弱護照不被接受。或者,顯示密碼/密碼強度並讓用戶決定。 –

+0

對於協議編號,我的意思是一個數字,最初用'0'表示,用salt和密碼散列保存,意思是:我們使用帶有SHA1的PBKDF2,16字節的鹽,64Ki的迭代和UTF-8編碼生成散列時的短語。您可能需要稍後進行升級,並且切換到另一種算法時,無法從存儲在數據庫中的信息中重新計算。 –