2012-03-20 35 views
3

我已經閱讀了大量有關如何存儲和比較輸入密碼的文章,一些舊的和一些新的,但都不同。因此,我採取了我認爲是最佳選擇並實施了以下內容。我的MVC3密碼處理足夠

請注意,我創建了自己的身份驗證提供程序,我沒有使用也沒有從標準MS MembershipProvider繼承(太臃腫)。這是一個輕量級網站,不需要「超級安全」,但我想遵循最佳做法。我只是想驗證一下,我沒有做任何明顯的錯誤或打開安全漏洞。另外,我查看了每個用戶的密碼salting,但它似乎過於複雜,我的需求。這是一個有效的假設嗎?

首先,我把下面的的appSettings關鍵在我的web.config,這是我的configurationProvider類返回。

​​

這裏是我的代碼是公開服務器驗證用戶和privatly 檢查反對什麼什麼被輸入以及進行編碼保存時的密碼存儲的密碼。我沒有顯示添加或更新密碼方法,因爲它們使用相同的私有方法。

public bool Authenticate(string emailAddress, string password, bool setAuthCookie = false) 
{ 
    bool isAuthenticated = false; 
    var member = _memberRepository.Find(m => m.Email == emailAddress).SingleOrDefault(); 
    if (member != null) 
    { 
    if (CheckPassword(password, member.Password)) 
    { 
     isAuthenticated = true; 
     FormsAuthentication.SetAuthCookie(emailAddress, setAuthCookie); 
    } 
    } 
    return isAuthenticated; 
} 

private bool CheckPassword(string providedPassword, string storedPassword) 
{ 
    return EncodePassword(providedPassword) == storedPassword; 
} 

private string EncodePassword(string password) 
{ 
    var hash = new HMACSHA1 
       { 
       Key = Encoding.ASCII.GetBytes(_configurationProvider.PasswordEncryptionKey) 
       }; 
    string encodedPassword = Convert.ToBase64String(hash.ComputeHash(Encoding.Unicode.GetBytes(password))); 
    return encodedPassword; 
} 

我唯一的規則是:

  • 密碼必須異步加密在數據庫中,無法解密。
  • 的方法應該是合理的安全對詞典或其他攻擊(我不是開發一個FDIC保險的網站,只是一個基本的內部網)

有了這個,有什麼明顯的我失蹤?

+1

如果您正在編寫一個Intranet站點,爲什麼不使用Windows安全? 你的提供者的方法是一個很好的方法。 MS的確是臃腫的。 瀏覽你的代碼它或多或少是我爲公共網站所做的,所以我認爲你對你寫的內容做得很好。記住,一旦它連接到一個網絡上,你就會展現出層層障礙,希望攻擊者感到無聊並消失,否則沒有什麼是真正安全的。 – Peter 2012-03-20 23:16:34

+0

對不起彼得......我應該澄清一點。這是一個基於互聯網的網站,但對於<100名成員將登錄的慈善團體。我應該使用「門戶」這個詞。所以它不會有大量的流量,但是我正在開發它,用於練習我的技能,所以我只想知道最佳實踐。 – bigmac 2012-03-20 23:20:32

+1

使用PBKDF2或bcrypt代替滾動自己的。 – 2012-03-21 01:02:10

回答

2

我相信你的方案是足夠的,但它可以改進一點點。

在我看來,你有一個salting scheme的開始,在你的app.config文件中有你的EncryptionKey。但是,爲了獲得最佳安全實踐,通常人們對每個密碼使用不同的salt,並將salt存儲在數據庫中。

class MyAuthClass { 
    private const int SaltSize = 40; 
    private ThreadLocal<HashAlgorithm> Hasher; 

    public MyAuthClass() 
    { 
    // This is 'ThreadLocal' so your methods which use this are thread-safe. 
    Hasher = new ThreadLocal<HashAlgorithm>( 
    () => new HMACSHA256(Encoding.ASCII.GetBytes(_configurationProvider.PasswordEncryptionKey) 
    ); 
    } 

    public User CreateUser(string email, string password) { 
    var rng = new RNGCryptoServiceProvider(); 
    var pwBytes = Encoding.Unicode.GetBytes(password); 
    var salt = new byte[SaltSize]; 
    rng.GetBytes(salt); 

    var hasher = Hasher.Value; 
    hasher.TransformBlock(salt, 0, SaltSize, salt, 0); 
    hasher.TransformFinalBlock(pwBytes, 0, pwBytes.Length); 
    var finalHash = hasher.Hash; 
    return new User { UserName = email, PasswordHash = finalHash, Salt = salt }; 
    } 

有了一個計劃,例如這一點,你的密碼變得更加複雜,因爲如果攻擊者碰巧獲得哈希,他就會有一個強力攻擊時也猜鹽。

這與您的配置文件中的EncodingKey具有相同的理念,但由於每個哈希都有自己的鹽,因此更安全。檢查輸入的密碼是類似的:

public bool IsPasswordCorrect(User u, string attempt) 
    { 
    var hasher = Hasher.Value; 
    var pwBytes = Encoding.Unicode.GetBytes(attempt); 
    hasher.TransformBlock(u.Salt, 0, u.Salt.Length, Salt, 0); 
    hasher.TransformFinalBlock(pwBytes, 0, pwBytes.Length); 
    // LINQ method that checks element equality. 
    return hasher.Hash.SequenceEqual(u.PasswordHash); 
    } 
} // end MyAuthClass 

當然,如果你寧願存儲哈希字符串而不是字節數組,歡迎您這樣做。

只是我的2美分!

+0

感謝您的輸入和代碼示例!我會做鹽。 – bigmac 2012-03-20 23:43:27

1

的一點想法...

  1. 您應該使用HMACSHA256或HMACSHA512代替HMACSHA1。 .NET4的默認散列算法現在是SHA256而不是SHA1。
  2. 當您獲取加密密鑰的字節時,使用ASCII編碼,但是您使用Unicode編碼計算散列。保持一致 - 使用Unicode。
  3. 考慮醃製你的散列。
  4. 你說加密,但你真的哈希。
+0

謝謝大衛。我將根據第1點和第2點更改我的代碼。對於第3點,我爲低容量網站做了足夠的工作,還是今天的標準確實需要醃製?我知道這是一個模棱兩可的問題,但會愛你的意見。對於第4點,有什麼區別? – bigmac 2012-03-20 23:23:33

+0

加密意味着有一個密鑰允許您加密和解密值。散列涉及單向操作 - 實際上沒有辦法解決價值(呃,並不是我理解它的簡單方法)。我不是安全專家 - 這些是我多年來不得不面對的事情。關於醃製,我一直認爲這是一個很好的做法。我想這實際上取決於您對安全級別和您正在存儲的數據類型的適應程度。如果是PCI數據,則可能需要將其鎖定。如果它是你的狗的照片,那麼,我想這取決於它。 :) – 2012-03-20 23:27:05

+0

我很高興我能夠幫助。祝你好運! – 2012-03-20 23:27:48