【问题标题】:Is this the way to salt and store a Password in Db?这是在数据库中加盐和存储密码的方法吗?
【发布时间】:2011-07-22 19:51:53
【问题描述】:

有几种方法(即使在 SO 中也是如此),他们都提到在数据库中保存密码的最佳方法是保存,不是密码,不是 hased 密码,而是 存储 salted 的哈希密码

我的问题很简单,放一些代码,这样是不是正确的方式?

string username = "myUsr";
string password = "myPwd";
DateTime createDate = DateTime.UtcNow;

// Salt it
string saltedPwd = String.Concat(password, createDate.Ticks.ToString());

// Hash it
HMACSHA1 hash = new HMACSHA1(Encoding.Unicode.GetBytes(Helper.EncryptKey));
string encodedPwd = Convert.ToBase64String(
                        hash.ComputeHash(Encoding.Unicode.GetBytes(saltedPwd)));

// Create User in the database
db.CreateUser(username, encodedPwd, createDate);

数据库用户表

user_id | username | password | create_date | last_access | active

在登录使用时再次执行该过程并检查encodedPwd 是否与提供的加盐密码相同。

我唯一担心的是,这是加盐密码的最佳方法吗? 可以使用创建日期(因为它总是会改变,我读到最好总是使用每次我们编码密码时都会有不同的salt...

或者应该是salt 一个完全不同的变量?

【问题讨论】:

  • 我会说这很好,我会很舒服。打败彩虹桌就足够了。
  • 你下次怎么验证密码,你的salt是UtcNow
  • @V4Vendetta 这是附加到 UserRow 中 CreateDate 的日期,所以我总是可以检查后者...日期也放在表中。
  • 您可以考虑另外使用用户 ID 对其进行加盐处理。这可以防止将数据库中的一个密码复制到另一行,从而更改用户的密码。
  • @balexandre:抱歉错过了,我唯一担心的是 ToString() 和 datetime 一样是特定于文化的,所以在这部分需要小心

标签: c# encryption hash passwords


【解决方案1】:

我想知道为什么还没有人提到BCrypt。有一个现成的 C# 实现。见http://derekslager.com/blog/posts/2007/10/bcrypt-dotnet-strong-password-hashing-for-dotnet-and-mono.ashx

如果您的问题有经过验证的解决方案,请不要重新发明轮子。

【讨论】:

  • 确实如此。像 bcrypt 或 PBKDF2 这样的密钥强化方案比普通散列更好。
  • 所以...如果它是一个开源项目,任何人都可以看到代码,就像我可以验证它是否是有效密码一样,黑客可以用他的彩虹列表做同样的事情吗?
  • @balexandre:我不明白你的顾虑。 您应该使用 bcrypt 或其他专业人士编写的库。如果可以避免的话,永远不要滚动自己的加密代码。它由专家设计,并且非常安全即使攻击者知道密码系统的源代码。这就是它的安全性。另外不要将时间用作盐。使用从加密的强熵源派生的字节作为盐,并将这些字节与散列密码一起存储。 (Bcrypt 会自动执行此操作。)
  • @Eric Lippert 因为这是我第一次跳过这种存储密码的方式,我认为这是一个合理的担忧:)
【解决方案2】:

我认为 CreateDate 的想法足够强大,但是当有人窃取您的数据库和代码时,您的盐就会暴露出来。基于“没有人可以扼杀我的代码”的安全性是不好的安全性。

您可以简单地对密码进行双重哈希...并使用第一次哈希的盐。

string Flavor(string passwd)
{
   string fhash = Str2SHA1(passwd);
   string salt = fhash[2] + fhash [10] + fhash[1]; // or whatever...
   string realhash = Str2SHA1(hash + salt);
}
string Str2Sha1(string str){ ... }

【讨论】:

    【解决方案3】:

    您的实现可能已经足够好,但最好使用熵更大的盐:您当前使用的刻度值将始终处于相对较小的范围内。

    我建议通过Rfc2898DeriveBytes 使用PBKDF2 之类的方式为您完成工作:

    string username = "myUsr";
    string password = "myPwd";
    
    using (var deriveBytes = new Rfc2898DeriveBytes(password, 20)) // 20-byte salt
    {
        byte[] salt = deriveBytes.Salt;
        byte[] key = deriveBytes.GetBytes(20); // 20-byte key
    
        string encodedSalt = Convert.ToBase64String(salt);
        string encodedKey = Convert.ToBase64String(key);
    
        // store encodedSalt and encodedKey in database
        // you could optionally skip the encoding and store the byte arrays directly
        db.CreateUser(username, encodedSalt, encodedKey);
    }
    

    然后进行身份验证...

    string username = "myUsr";
    string password = "myPwd";
    
    string encodedSalt, encodedKey;
    // load encodedSalt and encodedKey from database for the given username
    byte[] salt = Convert.FromBase64String(encodedSalt);
    byte[] key = Convert.FromBase64String(encodedKey);
    
    using (var deriveBytes = new Rfc2898DeriveBytes(password, salt))
    {
        byte[] testKey = deriveBytes.GetBytes(20); // 20-byte key
    
        if (!testKey.SequenceEqual(key))
            throw new InvalidOperationException("Password is invalid!");
    }
    

    【讨论】:

    • 在代码中使用静态salt key,而不是salt保存到数据库不是更好吗?
    • @Magnus:并允许发现盐的人为该位置使用的所有密码创建彩虹表? ;)
    • 数据库中没有盐会很容易找到吗?
    • @Magnus:盐好找没关系;重要的是每个用户都有一个独特的随机盐。即使攻击者可以访问所有代码和所有数据,一个好的身份验证方案也应该站得住脚。
    【解决方案4】:

    你试试:ProtectedData.Protect 方法怎么样?

    此方法可用于加密密码、密钥或连接字符串等数据。 optionalEntropy 参数使您可以添加数据以增加加密的复杂性;指定 null 表示没有额外的复杂性。如果提供,则在使用 Unprotect 方法解密数据时也必须使用此信息。

    【讨论】:

    • 坏主意。您仍然建议将密码存储在数据库中,尽管已加密。窃取源代码以及用户表,您就有了解密每个人密码的方法。
    • ProtectedData 用于加密和解密,而不是散列。
    • @tomfanning 您是否暗示存在一种解决方案,可以在“黑客”同时拥有数据库和源代码的情况下保护密码?
    • @Langdon:是的。散列。如果密码从一开始就没有存储,那么“黑客”就无法得到它。
    • 是的,当然。更具体地说,用盐散列。这就是这个问题的全部内容。切勿存储您的密码、句号、(可逆)加密或其他方式。
    【解决方案5】:

    您的方法完全没问题,但假设有人得到了您的数据库,但没有得到您的代码库。他们基本上可以弄清楚你只是连接密码和创建日期,他们可以对所有密码进行逆向工程。

    您可能希望进一步注入一个仅存在于您的代码库中的唯一字符串,以提供一些额外的保护。

    string username = "myUsr";
    string password = "myPwd";
    DateTime createDate = DateTime.UtcNow;
    
    // Salt it
    string saltedPwd = String.Concat(password, SomeOtherClass.StaticKey, createDate.Ticks.ToString());
    
    public class SomeOtherClass
    {
        public static string StaticKey = "#$%#$%superuniqueblahal$#%@#$43580"; // should probably be const/readonly, but whatever
    }
    

    【讨论】:

    • 嗯,值得商榷。充其量它会让他们提供盐,然后进行字典攻击。他们如何能够看到使用的创建日期?此外,他们不知道创建日期是如何格式化来创建盐的。
    • 他们可以谷歌作者的名字并找到这个 StackOverflow 问题。 :) 或者更现实地说,他们可以使用用户行上的数据暴力破解大量常用方法。
    • 难道他们不需要知道Helper.EncryptKey(哈希键)才能使用哈希吗?
    • 所以有两种选择,混合使用静态和动态盐,或者将动态盐作为散列的一部分撒在主密码中,而不是将其附加到前面或后面。跨度>
    • @Langdon 但是StaticKey 我用作哈希键,这不是很好的方法吗?我还需要为盐添加它吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-11-18
    • 2021-03-18
    • 2020-05-13
    • 1970-01-01
    • 2015-11-11
    • 2012-02-16
    • 2012-10-21
    相关资源
    最近更新 更多