我是C#的新手,这是我的第一个问题,所以我提前为任何失礼道歉。
上下文
当用户注册时,我调用CreateSaltedHash()方法并从文本字段中传递用户输入的密码。在将密码存储在User表的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.
}
}
在Register类中调用该方法。
User user= new User();
user.password = PasswordHash.CreateSaltedHash(TextBoxUserPassword.Text);
答案 0 :(得分:3)
你可以使用Bcrypt.Net;它有很多非常安全的建议,而且非常容易使用。据我了解,当您创建密码时,它会自动为您生成一个唯一的盐,然后将其存储在哈希密码字符串中;因此,您不要单独存储salt,而是在与散列密码相同的字段中存储。重点是每个密码都有自己的盐,这使得黑客破解多个密码变得更加困难(耗时)。 Bcrypt使用的算法也是CPU密集型的,因此需要大量的计算能力(=金钱)才能破解。
Jeff Atwood(stackoverflow主持人)recommends Bcrypt。
答案 1 :(得分:2)
当您第一次生成哈希时,您需要存储salt和最终哈希值 - 然后重新使用相同的盐进行比较。
因此,您需要更改CreateSaltedHash
方法以获取密码和salt,并编写新的CreateSalt
方法以在创建/更改密码时生成salt,该密码与最终密码一起存储散列。
答案 2 :(得分: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 ;
}
答案 3 :(得分:1)
您必须将盐与哈希一起存储。
请参阅此文章以获取一些背景信息: http://www.h-online.com/security/features/Storing-passwords-in-uncrackable-form-1255576.html
答案 4 :(得分:1)
正如另一个回答的那样;是的,你应该存储盐或从例如用户名派生它。
您还应该使用Rfc2898DeriveBytes
来提高其安全性。
这是一篇关于该主题的好文章: Password salt and hashing in C#
答案 5 :(得分:0)
我应该将盐储存在单独的栏中吗?
是
生成盐渍哈希时我应该使用分隔符吗?
没有必要,但只要在验证时包含相同的分隔符,它也不会受到伤害。
根据盐渍和散列密码验证输入密码的最安全方法是什么?
SHA512,SHA-2或-3比SHA256更安全,但你需要更高的安全性吗?
答案 6 :(得分:0)
您可以存储盐,也可以每次使用相同的盐。我建议存储盐,因为它比在所有用户中使用相同的盐更安全。
我的大多数表都有一个列,其中包含创建行的日期时间。我使用此值的DateTime
结构的Ticks属性来对哈希进行加盐,但您可以使用任何您喜欢的内容,只要您每次都为用户使用相同的salt即可。需要注意的一件事是,如果您使用此方法,并且您使用的是SQL类型DateTime(而不是DateTime2),则存在精度问题。如果您在代码中创建DateTime
,则需要截断它(我相信百分之一秒)。
答案 7 :(得分:0)
对于更长的答案 -
创建的每个密码都应该是随机的。这将使其本身独一无二。由于这种“随机性”,您几乎永远无法以编程方式找到与文件关联的哈希值。
加密密码的方式(没有哈希)应该是相同的,所以每次使用这种方法的反向就足够了。
PS:(更安全的验证方式)您可以将加密密码反转为原始 [Hard] ,或者使用哈希加密验证密码,并确保加密的密码与存储在DB [Preferred] 中的密码相匹配。
因此,您需要将加密的密码以及与之关联的哈希值存储在数据库中。
这将是收集验证密码所需的所有信息的方法。
答案 8 :(得分:0)
因为您要生成随机盐,所以需要将salt存储在数据库中。问题在于,如果您的数据库受到攻击,攻击者将拥有salt和哈希密码,因此他们可以更轻松地确定真实密码。理想情况下,您的代码中应该有一个静态salt,这样如果您的数据库被泄露,他们仍然没有盐,如果您的代码被泄露,他们还没有数据库。
另一个解决方案可能是使用胡椒。 Pepper类似于salt,但您不会使用salt和哈希密码将数据库存储在数据库中。它将存储在代码中。这样,您将生成一个随机盐,并将一个常量分开存储。为了使胡椒更随机,您可以创建一个更大的字符串的子字符串,用于胡椒,该胡椒根据某些变量(例如用户ID)进行偏移。这又是一个内部事件,如果他们设法获取您的数据,攻击就不会知道。
答案 9 :(得分:0)
你应该保存消化物和盐。迭代和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);
}
最近的文章建议,为了进一步保护密码,您可以将密码拆分为多个部分,对各个部分进行散列,然后将它们存储在数据库中的单独表中。