我使用.NET System.Security.Cryptography类散列密码.它有一些散列算法,例如MD5,SHA1,SHA256,SHA384,SHA512
结果散列值是一个字节数组.我应该将它转换为十六进制字符串进行存储,或Convert.ToBase64String(),还是其他东西?(我喜欢Base64,因为它比Hex短).
顺便提一下,有很多哈希算法可供选择,我随机选择了SHA384,但有哪一个"更好"或适合这项任务?
请评论.
阅读前八条评论后更新:
通过答案和我已经完成的进一步阅读,似乎MD5,SHA1或多或少相当(SHA1稍微更安全).SHA256,384,512在递增顺序时提供更好的安全性.
由于我不需要fort-knox(这是一个没有网址,浏览器,互联网,内联网或外联网的内部企业系统),我将绕过"腌制"业务 - 我想如果有人可以窃取密码表,他们也可以窃取其他表中的实际数据.
但我将保留"盐"概念以供将来参考; 不确定在散列之前是否应该在密码附加(最后)或预先(在前面)附加盐,它会产生影响吗?此外我还在考虑使用密码本身的前几个字符作为盐,以避免额外的字段来存储它,但我想它不够长 - 并且盐应该足够长.
共识认为base64转换是存储和比较的合理选择.鉴于最大密码长度为15个字符,我仍然需要弄清楚哈希存储需要的最大数据库列长度是多少.也许Varchar(64)?
谢谢大家的贡献.
Pav*_*pin 12
即使你的解决方案不是堡垒,你应该勤奋并实施腌制.因为许多人在其他地方重复使用他们的密码,并且如果攻击者选择将破解的密码数据库用于其他目的,那么闯入将在组织外部造成额外的损害.
Salting使字典攻击更加昂贵.通过决定使用哪种盐大小,您可以微调您的机会.以下是Bruce Schneier的"应用密码学"的引用:
"Salt不是灵丹妙药;增加盐位的数量并不能解决所有问题.Salt只能防止对密码文件的一般字典攻击,而不是对单个密码的协同攻击.它可以保护密码相同的人.多台机器,但不会让选择不当的密码更好."
这是C#中的一个示例.这并不难.您可以选择要使用的盐大小和哈希函数.免责声明:如果您真的关心密码完整性,请使用像bcrypt这样的东西.
using System;
using System.IO;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
public class PassHash {
private static readonly RandomNumberGenerator rng = RandomNumberGenerator.Create();
public static readonly int DefaultSaltSize = 8; // 64-bit salt
public readonly byte[] Salt;
public readonly byte[] Passhash;
internal PassHash(byte[] salt, byte[] passhash) {
Salt = salt;
Passhash = passhash;
}
public override String ToString() {
return String.Format("{{'salt': '{0}', 'passhash': '{1}'}}",
Convert.ToBase64String(Salt),
Convert.ToBase64String(Passhash));
}
public static PassHash Encode<HA>(String password) where HA : HashAlgorithm {
return Encode<HA>(password, DefaultSaltSize);
}
public static PassHash Encode<HA>(String password, int saltSize) where HA : HashAlgorithm {
return Encode<HA>(password, GenerateSalt(saltSize));
}
private static PassHash Encode<HA>(string password, byte[] salt) where HA : HashAlgorithm {
BindingFlags publicStatic = BindingFlags.Public | BindingFlags.Static;
MethodInfo hasher_factory = typeof (HA).GetMethod("Create", publicStatic, Type.DefaultBinder, Type.EmptyTypes, null);
using (HashAlgorithm hasher = (HashAlgorithm) hasher_factory.Invoke(null, null))
{
using (MemoryStream hashInput = new MemoryStream())
{
hashInput.Write(salt, 0, salt.Length);
byte[] passwordBytes = Encoding.UTF8.GetBytes(password);
hashInput.Write(passwordBytes, 0, passwordBytes.Length);
hashInput.Seek(0, SeekOrigin.Begin);
byte[] passhash = hasher.ComputeHash(hashInput);
return new PassHash(salt, passhash);
}
}
}
private static byte[] GenerateSalt(int saltSize) {
// This generates salt.
// Rephrasing Schneier:
// "salt" is a random string of bytes that is
// combined with password bytes before being
// operated by the one-way function.
byte[] salt = new byte[saltSize];
rng.GetBytes(salt);
return salt;
}
public static bool Verify<HA>(string password, byte[] salt, byte[] passhash) where HA : HashAlgorithm {
// OMG: I don't know how to compare byte arrays in C#.
return Encode<HA>(password, salt).ToString() == new PassHash(salt, passhash).ToString();
}
}
Run Code Online (Sandbox Code Playgroud)
用法:
新用户提交其凭据.
PassHash ph = PassHash.Encode<SHA384>(new_user_password);
Store ph.Salt&ph.Passhashsome ...稍后,当用户再次登录时,您将查找具有salt&passhash的用户记录,然后执行以下操作:
PassHash.Verify<SHA384>(user_login_password, user_rec.salt, user_rec.passhash)
}
Base64确实比hex更短,如果它最终在URL中(由于涉及符号),你需要小心.这取决于你使用它的地方,基本上.Hex通常具有以下优点:用肉眼更容易"读取",但因为这基本上是无意义的数据,这与散列无关.(如果需要,您可以随时使用在线base64解码.)
当然,这就是假设你想要一个字符串.与不透明的字节数组相比,字符串很好,易于存储,传输等.