验证一个加盐哈希值

9

我是一名新手,第一次在这里提问,如果有任何失礼之处,请您谅解。

背景:

当用户注册时,我会调用CreateSaltedHash()方法并传入来自文本字段的用户输入密码。此方法会对密码进行盐值加密后再将其存储在我的用户表的Password列中。

问题:

当用户尝试登录时,应该如何验证密码?

如果我再次调用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);

应该将盐值存储在单独的列中吗?- 是的。然后引用该列。 - TheGeekZn
你应该明白你的哈希方法存在严重问题,对吧?你真的应该使用一个哈希算法来保护密码,你目前的选择不是一个好选择。 - Security Hound
我已经编辑了你的标题。请参考“问题的标题应该包含“标签”吗?”,在那里达成共识是“不应该”。 - John Saunders
2
@Ramhound,你能解释一下当前方法有什么问题,以及一个好的选择是什么吗?谢谢。 - Naomi Owens
10个回答

3
你可以使用Bcrypt.Net;它有很多建议,可以更加安全,而且非常容易使用。据我所知,当您创建密码时,它会自动生成一个唯一的盐并将其存储在哈希密码字符串中;因此,您不需要单独存储盐,而是将其存储在与哈希密码相同的字段中。重点是每个密码都有自己的盐,这使得黑客破解多个密码变得更加困难(耗时)。Bcrypt使用的算法也需要大量计算能力(=金钱)才能破解。
Jeff Atwood(stackoverflow主持人)推荐使用Bcrypt

2

当您首次生成哈希时,需要同时存储盐和最终哈希 - 然后将相同的盐用于未来的比较。

因此,您需要更改CreateSaltedHash方法以接受密码和盐,并编写一个新的CreateSalt方法,在创建/更改密码时生成盐,并将其与最终哈希一起存储。


2
我建议您使用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 ;
}

1

1

正如其他答案所述,是的,您应该存储盐或从用户名中派生它。

您还应该使用{{link1:Rfc2898DeriveBytes}}使其更安全。

这是一篇关于该主题的好文章: {{link2:C#中的密码盐和哈希}}


0
你应该保存摘要和盐。迭代次数和摘要长度的值可以在你的应用程序中作为常量。
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);
}

最近的文章建议为了进一步保护密码,可以将密码分成几部分,对每个部分进行哈希处理,然后将它们存储在数据库中的不同表中。

0
我应该将盐分存储在单独的列中吗?
是的。
生成盐哈希时,我应该使用分隔符吗?
不是必需的,但只要在验证时包含相同的分隔符,它也不会有害。
验证输入密码与盐和哈希密码之间最安全的方法是什么?
SHA512、SHA-2或-3比SHA256更安全,但您需要更高的安全性吗?

0

你可以存储盐,或者每次使用相同的盐。我建议存储盐,因为这比在所有用户之间使用相同的盐更安全。

我的大多数表都有一个列,其中包含行创建的日期时间。我使用此值的DateTime结构的Ticks属性来加盐哈希,但您可以使用任何您喜欢的东西,只要您每次为用户使用相同的盐。需要注意的一件事是,如果您使用此方法,并且使用SQL类型DateTime(而不是DateTime2),则存在精度问题。如果您在代码中创建DateTime,则需要将其截断(我认为是到百分之一秒)。


0

对于更长的答案 -

哈希应该在每个密码创建时都是随机的。这将使它本身独特。由于这种“随机性”,你几乎永远无法编程地找到与文件相关联的哈希。

加密密码的方式(不使用哈希)应该是相同的,因此每次使用该方法的反向操作就足够了。
PS:(一种更安全的验证方式)您可以将加密后的密码反转为其原始状态 [Hard] ,或者使用哈希加密验证密码,并确保加密密码与存储在数据库中的密码匹配 [首选]

因此,您需要将加密密码以及与之关联的哈希存储在数据库中。

这将是收集验证密码所需的所有信息的方法。


0
由于您正在生成随机 salt,您需要将 salt 存储在数据库中。问题在于,如果您的数据库被攻击者攻陷,则攻击者将拥有 salt 和散列密码,因此他们可以更轻松地确定真实密码。理想情况下,您应该在代码中拥有一个静态 salt,这样,如果您的数据库被攻破,他们仍然没有 salt,如果您的代码被攻破,他们还没有获得数据库。
另一种解决方案可能是使用 pepper。Pepper 类似于 salt,但您不会将其与 salt 和散列密码一起存储在数据库中。它将存储在代码中。这样,您就有了一个生成的随机 salt 和一个存储分离的常量。为使 pepper 更随机,您可以创建一个基于某个变量(例如用户 ID)的偏移的较大字符串的子字符串,用于 pepper。这又是攻击者不知道的内部事物,即使他们设法获取您的数据。

1
如果每个密码都有不同的唯一盐值,那么黑客破解多个密码就变得更加困难。 - Polyfun
是的,但是你需要在某个地方存储唯一的盐值,如果它们在数据库中,黑客也会得到它们,所以它们不同的事实就变得无关紧要了。 - Adam Goss
3
如果盐值是独一无二的,那么使用像bcrypt这样的方法破解多个密码就会变得更加昂贵 - 请参考http://www.codinghorror.com/blog/2012/04/speed-hashing.html。 - Polyfun
很好,我之前不熟悉bcrypt,看起来很有趣。我会进一步调查并可能采用它。不过,考虑到您要加密的内容的价值也是很重要的。根据您存储的内容,这可能过于复杂了。 - Adam Goss

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接