2012-11-15 129 views
9

我是C#的新手,這是我的第一個問題,所以我提前爲任何失禮道歉。驗證鹽漬散列

上下文:

當用戶寄存器我所說的CreateSaltedHash()方法和從文本字段傳遞給它用戶輸入的密碼。此方法在將密碼存儲在我的用戶表的密碼列中之前將其密碼存儲並散列化。

問:

我應該如何驗證密碼,當用戶試圖登錄?

如果我再次調用CreateSaltedHash()方法,它將因爲隨機鹽而不匹配。

我應該將鹽儲存在一個單獨的列中嗎?生成鹽漬散列時,我應該使用分隔符嗎?根據鹽漬和散列密碼驗證輸入密碼的最安全方法是什麼?

代碼: 這是我到目前爲止所。

public class PasswordHash 
{ 
    public const int SALT_BYTES = 32; 

    /* 
    * Method to create a salted hash 
    */ 
    public static byte[] CreateSaltedHash(string password) 
    { 
     RNGCryptoServiceProvider randromNumberGenerator = new RNGCryptoServiceProvider(); 
     byte[] salt = new byte[SALT_BYTES]; 
     randromNumberGenerator.GetBytes(salt); 
     HashAlgorithm hashAlgorithm = new SHA256Managed(); 
     byte[] passwordByteArray = Encoding.UTF8.GetBytes(password); 
     byte[] passwordAndSalt = new byte[passwordByteArray.Length + SALT_BYTES]; 
     for (int i = 0; i < passwordByteArray.Length; i++) 
     { 
      passwordAndSalt[i] = passwordByteArray[i]; 
     } 
     for (int i = 0; i < salt.Length; i++) 
     { 
      passwordAndSalt[passwordByteArray.Length + i] = salt[i]; 
     } 
     return hashAlgorithm.ComputeHash(passwordAndSalt); 
    } 

    public static bool OkPassword(string password) 
    { 
     //This is where I want to validate the password before logging in. 
    } 
} 


調用在註冊類中的方法。

User user= new User(); 
user.password = PasswordHash.CreateSaltedHash(TextBoxUserPassword.Text); 
+0

'我應該將鹽儲存在單獨的列中嗎 - 是的。然後引用那個。 – TheGeekZn

+0

你理解你的hasing方法有一個嚴重的問題嗎?你真的應該使用hasing算法設計來保護密碼,你現在的選擇不是一個好的選擇。 –

+0

我編輯了你的標題。請參閱:「[應該在其標題中包含」標籤「](http://meta.stackexchange.com/questions/19190/)」,其中的共識是「不,他們不應該」。 –

回答

1

當您第一次生成散列時,您需要同時存儲鹽和最終散列 - 然後重新使用相同的鹽以備將來比較。

所以,你會改變你的CreateSaltedHash法取密碼和鹽,譜寫新的CreateSalt方法是創建一個口令時生成鹽/改變了存儲旁邊的最終散列。

0

我應該將鹽儲存在一個單獨的列中嗎?

是。

生成鹽漬散列時我應該使用分隔符嗎?

沒有必要,但它也不會傷害,只要在驗證時包含相同的分隔符。

根據鹽漬和散列密碼驗證輸入密碼最安全的方法是什麼?

SHA512,SHA-2或-3比SHA256更安全,但您是否需要更高的安全性?

0

您可以儲存鹽,或使用相同的鹽每一次。我建議存儲鹽,因爲它比所有用戶使用相同的鹽更安全。

我的大多數表格都有一個包含創建行時的日期時間的列。我使用此值的DateTime結構的Ticks屬性來對散列進行加鹽處理,但只要每次爲用戶使用相同的鹽,就可以使用任何您喜歡的任何東西。有一點需要注意的是,如果你使用這種方法,並且你正在使用SQL類型的DateTime(而不是DateTime2),那麼存在一個精確的問題。如果在代碼中創建DateTime,則需要截斷它(我相信是百分之一秒)。

0

而對於較長的答案 -

的已經應該是隨機的與創建的每個密碼。這將使其本身具有獨特性。由於這種「隨機性」,您將幾乎永遠無法以編程方式查找與該文件關聯的散列。

你加密密碼的方式(沒有散列)應該是一樣的,所以每次使用這種方法的反向就足夠了。
PS:(O驗證更安全的方法)您可以扭轉加密的密碼到其原始[硬盤],或加密與哈希驗證密碼,並確保加密口令匹配存儲在DB [優選]

因此,您需要在數據庫中存儲加密密碼以及與之關聯的散列。

這將是收集所有驗證密碼所需信息的方式。

0

由於您正在生成隨機鹽,因此您需要將salt存儲在數據庫中。問題在於,如果數據庫變得受到攻擊,攻擊者將擁有salt和哈希密碼,因此他們可以更輕鬆地確定真實密碼。理想情況下,你的代碼應該有一個靜態鹽,這樣如果你的數據庫受到威脅,他們仍然沒有鹽,並且如果你的代碼受到攻擊,他們還沒有數據庫。

另一種解決方案可能是使用胡椒。胡椒類似於鹽,但你不用鹽和散列密碼將它存儲在數據庫中。它將被存儲在代碼中。這樣你就有了一個隨機生成的鹽和一個單獨存儲的常量。爲了讓胡椒更隨機,你可以創建一個更大的字符串的子字符串,用於胡椒根據某個變量(如用戶ID)進行偏移。這又是一個內部的東西,攻擊者不知道他們是否設法獲取你的數據。

+1

如果你對每個密碼都有不同的獨特鹽分,那麼黑客就很難破解多個passords。 – Polyfun

+0

是的,但你需要將獨特的鹽存儲在某個地方,如果它們在數據庫上,那麼黑客就會擁有它們,因此它們不同的事實就不相關了。 –

+3

並非如此,因爲如果鹽是獨特的,則破解多個密碼變得更加昂貴,特別是使用類似bcrypt的東西 - 請參閱http://www.codinghorror.com/blog/2012/04/speed-hashing.html 。 – Polyfun

3

你可以使用Bcrypt.Net;它有很多建議是非常安全的,再加上它非常易於使用。據我瞭解,當你創建密碼時,它會自動爲你生成一個唯一的salt,然後存儲在散列的密碼字符串中;所以你不要單獨存儲鹽,而是在散列密碼的相同字段中。關鍵是每個密碼都有它自己的鹽,這使得黑客破解多個密碼變得更加困難(耗時)。 Bcrypt使用的算法也是CPU密集型的,所以它需要大量的計算能力(=金錢)才能破解。

Jeff Atwood(stackoverflow moderator)recommends Bcrypt

0

您應該保存摘要和鹽。 iterations和digestLength值可以是應用程序中的常量。

byte[] getNewSalt(Int32 size) 
{ 
    RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider(); 
    byte[] salt = new byte[size]; 
    rng.GetBytes(salt); 
    return salt; 
} 


byte[] getPasswordDigest(byte[] value, byte[] salt, Int32 iterations, Int32 digestLength) 
{ 
    Rfc2898DeriveBytes deriveBytes = new Rfc2898DeriveBytes(value, salt, iterations); 
    return deriveBytes.GetBytes(digestLength); 
} 

最近的文章建議,要進一步的安全密碼,您可以將密碼拆分成幾部分,哈希的各個部分,然後將它們存儲在數據庫單獨的表。

1

我建議你使用ServiceStack的SaltedHash,你可以從你的Nuget安裝它。 只需在您的Nuget控制檯中輸入Install-Package ServiceStack,然後您就可以在代碼中使用以下導入。

using ServiceStack.ServiceInterface.Auth; 

然後你會產生你的鹽和散列這麼多容易絕對更快比以前。 只需輸入以下代碼:

class Security 
{ 
    ... 
    public void generate(string Password) 
    { 
    string hash, salt; 
    new SaltedHash().GetHashAndSaltString(Password,out hash,out salt); 
    //Store the hash and salt 
    } 
    ... 
} 

而且,你必須哈希和鹽能夠運行您的OkPassword方法。

public bool OkPassword(string Password) 
{ 
    var hash = //getStoredHash 
    var salt = //getStoredSalt 
    bool verify = new SaltedHash().VerifyHashString(Password, hash , salt); 
    return verify ; 
}