2014-03-28 176 views
0

我對使用登錄名保護密碼有疑問。使用登錄名保護密碼

  1. 您使用共享salt來登錄登錄名,然後使用BCrypt對其進行哈希處理。

  2. 然後,您將原始純文本登錄名稱作爲密鑰使用AES加密密碼。然後用獨特的鹽醃製,最後與BCrypt混合。

  3. 創建帳戶時,用戶的顯示名稱設置爲其用戶ID(整數),而不是其登錄名。用戶可以稍後更改它,除非它不能匹配任何其他人的顯示名稱,或者與大小寫不敏感的比較與其登錄名稱過於緊密匹配。

    我的問題是,如果數據庫遭到破壞,這是否會使恢復密碼比存儲純文本用戶名和獨特鹽漬和BRcypted密碼困難得多?

+0

如果您的管理員離開並接收數據庫,則無法更改某些安全參數。退出的管理員將擁有用戶名,密碼以及恢復它們的方法。 – jww

+0

你指的是什麼安全參數?鹽以及散列的登錄名和密碼將全部存儲在數據庫中。 – antelopecarbuilding395

+0

身份驗證如何? – Gumbo

回答

0

此過程看起來非常複雜。實際上,您正在向存儲的散列(您的散列密碼的過程)添加服務器端祕密。只要程序保密,這確實會增加安全性。換句話說,如果攻擊者只能讀取數據庫的訪問權限(SQL注入),那麼您有一個優勢,只要他擁有服務器上的權限並知道代碼,就沒有任何優勢。

雖然有一個更容易和更安全的方式來獲得這種優勢。只需用隨機鹽計算BCrypt哈希(大多數實現都會這樣做),然後使用服務器端密鑰(例如AES)對此哈希值進行加密。密鑰不應該從其他參數詮釋,而應該使用足夠長且足夠隨機的密鑰。

我試圖解釋我的教程中關於safely storing passwords的原因,也許你想看看它。

編輯:

好吧,我現在明白了,你要處理的登錄名像第二密碼,並不會存儲它明文,而不是你只存儲它的BCrypt哈希與全球「鹽」。

讓我們假設您願意花費1秒CPU時間進行密碼散列。在你的方案中,你必須將其分成半秒以散列登錄名和半秒密碼。攻擊者必須第一步暴力登錄名和密碼。

  1. 登錄名通常是非常弱的密碼。雖然人們知道密碼需要強大,但登錄名通常只包含一個帶有數字的名稱並且很短。
  2. 您需要使用登錄名的哈希來查找數據庫記錄,因此您不能使用隨機鹽,而需要使用全局「鹽」。這允許構建一個單一的彩虹表來一次性破解所有用戶名。可以預先計算重要的登錄名,如「admin」。
  3. 爲了避免重複,您必須使用大寫/小寫的登錄名,這會進一步縮小搜索空間。

這意味着您花費了半秒鐘的時間處理一個非常弱的密碼(登錄名),然後半秒鐘的正常密碼。與正常(希望強大)的密碼投資1秒相比,您可能會降低安全性,但無論如何我看不出任何優勢。

+0

「實際上,您正在向存儲的散列(您的散列密碼的過程)添加服務器端祕密。」 – antelopecarbuilding395

+0

數據庫1:用戶名用共享鹽和BCrypted進行鹽化。密碼用AES加密,因爲用戶名是加密密鑰,然後用獨特的隨機鹽和BCrypted進行鹽析。 數據庫2.用戶名是明文。密碼用獨特的鹽醃製,並與BCrypt混合。 假設您知道兩個數據庫如何存儲用戶名/密碼,並且在這兩種情況下都知道該進程的每個細節,那麼使用第一個數據庫恢復密碼的難度會不會更大? – antelopecarbuilding395

+0

@ antelopecarbuilding395 - 你獲得很少。如果您使用BCrypt作爲哈希算法,攻擊者必須蠻力或嘗試字典攻擊。 BCrypt將在每次猜測時使用很多時間來保護密碼。您的第一步可以爲每個用戶完成一次(使用恆定的鹽和用戶名並對它進行加密),因此它們不計數。在散列之前對密碼進行加密需要一點時間,但是您可以更好地投資於增加BCrypt的成本因子。使用用戶名永遠不會比使用隨機鹽和強大的獨立密鑰更好。 – martinstoeckli

0

驗證碼看起來像這樣。

public static LoginResult TryLogin(string loginName, string pwd) 
    { 
     string loginHash = BCrypt.Net.BCrypt.HashPassword(loginName, SHARED_SALT); 

     WidgetDataContext dc = new WidgetDataContext(); 
     var record = (from rec in dc.usp_GetUserByLoginName(loginHash) 
         select rec).SingleOrDefault(); 

     if (record == null) 
      return new LoginResult(null, "Invalid Login Name/Password"); 

     if (record.FailedLoginCount >= MAX_CONSECUTIVE_LOGIN_FAILURES) 
      return new LoginResult(null, "You have exceeded your maximum number of Login failures. Your account is locked."); 

     if (record.Locked) // In case account is locked for another reason 
      return new LoginResult(null, "Your Account is locked."); 

     pwd = EncryptionServices.Encrypt(pwd, loginName); 
     pwd = BCrypt.Net.BCrypt.HashPassword(pwd, record.Salt); 

     if (pwd == record.Password) 
     { 
      record.FailedLoginCount = 0; 
      dc.SubmitChanges(); 
      return new LoginResult(record.UserId, "Login Successful"); 
     } 

     record.FailedLoginCount++; 
     dc.SubmitChanges(); 

     return new LoginResult(null, "Invalid Login Name/Password"); 
    } 
+0

只需澄清一下,我可以很快地輸入該示例。它的目的是作爲一個缺乏評論的模型,非描述性的變量名稱(如pwd而不是密碼),並且在很多方面都過於簡化。我想詳細說明一下,並要求任何查看代碼的人都考慮這一點。 – antelopecarbuilding395