【发布时间】:2010-08-03 02:35:34
【问题描述】:
一旦我生成了盐并对密码进行哈希处理(使用 bcrypt 等),最好将结果作为字符串或字节数组存储在数据库中?这两种方式有什么好处吗?还是更主观的决定?
【问题讨论】:
-
我从未见过有人使用过除标准 varchar 字符串之外的任何东西。
标签: c# hash password-protection salt
一旦我生成了盐并对密码进行哈希处理(使用 bcrypt 等),最好将结果作为字符串或字节数组存储在数据库中?这两种方式有什么好处吗?还是更主观的决定?
【问题讨论】:
标签: c# hash password-protection salt
如果您使用 VARCHAR,请确保字节是有效字符或在保存哈希和盐之前将字节强制为 ASCII。为了更安全地避免数据损坏,您可能希望在将字节存储到 VARCHAR 字段之前将它们编码为 Base64 或十六进制。
例如,如果您存储来自MD5 或SHA1 的byte[] 输出,则在将byte[] 转换为文本时,某些字节值可能会被删除或替换,如果它们不是有效的UTF-8/统一码。如果您使用 ASCII,这应该不是什么大问题,但它仍然可能导致数据损坏。
如果您不想处理 Hex,可以使用框架中内置的 base64 方法 Convert.ToBase64String。
如果您使用 VARBINARY 并且不尝试使用文本编码方法,则此问题不应该存在,但可能会使比较哈希变得更加困难。
...请注意,如果您使用NVARCHAR,仍可能会发生此数据损坏...
static void Main(string[] args)
{
var salt = "MySalt";
var password = "MyPassword";
var saltedKey = CalculateHash(salt, password);
Console.WriteLine(saltedKey);
// MySalt$teGOpFi57nENIRifSW3m1RQndiU=
var checkHash = CheckHash(saltedKey, password);
Console.WriteLine(checkHash);
// True
}
private static string CalculateHash(string saltOrSaltedKey, string password)
{
var salt =
saltOrSaltedKey.Contains('$')
? saltOrSaltedKey.Substring(0, saltOrSaltedKey.IndexOf('$') + 1)
: saltOrSaltedKey + '$';
var newKey = Encoding.UTF8.GetBytes(salt + password);
var sha1 = SHA1.Create();
sha1.Initialize();
var result = sha1.ComputeHash(newKey);
// if you replace this base64 version with one of the encoding
// classes this will become corrupt due to nulls and other
// control character values in the byte[]
var outval = salt + Convert.ToBase64String(result);
return outval;
}
private static bool CheckHash(string saltedKey, string password)
{
var outval = CalculateHash(saltedKey, password);
return outval == saltedKey;
}
【讨论】:
VARCHAR 应该足够了。在我看来,与二进制相比,VARCHAR 数据更易于使用和使用。
内置的 ASP.NET 成员资格提供程序还将散列密码存储为 VARCHAR 字段。
【讨论】:
大多数 md5/sha1 库返回 base64 编码的哈希值,在这种情况下 varchar 就足够了
【讨论】: