在当今数字化时代,密码安全是保护用户数据不被非法访问的关键。尽管经常听到各种密码泄露事件,但通过采用合适的哈希算法,可以显著提高密码存储的安全性。BCrypt是一种广泛使用的密码哈希算法,它通过故意降低处理速度来抵抗暴力破解攻击。本文将介绍BCrypt算法的基本概念、优点,以及如何在C#.NET和SQL Server中实现密码的安全存储。
BCrypt算法因其独特的设计而受到青睐。首先,它运行缓慢,这使得暴力破解变得困难。其次,BCrypt的输出使用Base-64编码,这意味着它不包含难以存储在简单字符字段中的字符;编码页(CodePage)变得无关紧要。
虽然BCrypt算法本身是可靠的,但不同的实现可能会有缺陷。例如,2011年发现的一个缺陷影响了Unix版本的BCrypt实现。为了确保使用的是可靠的实现,可以选择Derek Slager的C#实现,它似乎没有这个缺陷。即使存在缺陷,由于用户都位于美国,并且不太可能使用标准键盘无法直接输入的密码字符(ASCII值大于127的字符),因此这个缺陷对来说影响不大。
BCrypt算法包括一个生成盐(salt)的方法。当盐应用于密码时,生成的哈希值包含了原始盐和哈希后的密码。可以将盐和密码组合存储在数据库的CHAR(60)字段中。不需要将哈希密码与盐分开存储,也不应该这样做,因为BCrypt类包含一个方法,它期望盐和密码组合作为参数传递,以便以后确认用户输入的密码是否正确。
盐总是以类似$2a$10$开头,表示BCrypt的2a版本和10轮计算。10轮是默认值,可以选择更大的数字使其更慢,或者更小的数字使其更快,但10对于大多数人来说是一个非常好的选择。由于盐的其余部分是22个字节,$2a$10$是7个字节,总共是29个字节,哈希密码总是剩余的31个字节。将存储在数据库中的输出的总长度总是60个字节长。
string myPassword = "password";
string mySalt = BCrypt.GenerateSalt();
// mySalt == "$2a$10$rBV2JDeWW3.vKyeQcM8fFO"
string myHash = BCrypt.HashPassword(myPassword, mySalt);
// myHash == "$2a$10$rBV2JDeWW3.vKyeQcM8fFO4777l4bVeQgDL6VIkxqlzQ7TCalQvla"
bool doesPasswordMatch = BCrypt.CheckPassword(myPassword, myHash);
每个存储的密码都会有一个不同的盐,每次用户更改密码时,都会为用户生成一个新的盐。还鼓励向密码中添加一点硬编码的盐。这种硬编码的盐为黑客增加了一点挑战,即使他们窃取了数据库,但没有窃取代码,也不知道硬编码的盐。
private void SetPassword(string user, string userPassword)
{
string pwdToHash = userPassword + "^Y8~JJ"; // ^Y8~JJ 是硬编码盐
string hashToStoreInDatabase = BCrypt.HashPassword(pwdToHash, BCrypt.GenerateSalt());
using (SqlConnection sqlConn = new System.Data.SqlClient.SqlConnection(...))
{
sqlConn.Open();
SqlCommand cmSql = sqlConn.CreateCommand();
cmSql.CommandText = "UPDATE LOGINS SET PASSWORD=@parm1 WHERE USERNAME=@parm2";
cmSql.Parameters.Add("@parm1", SqlDbType.Char);
cmSql.Parameters.Add("@parm2", SqlDbType.VarChar);
cmSql.Parameters["@parm1"].Value = hashToStoreInDatabase;
cmSql.Parameters["@parm2"].Value = user;
cmSql.ExecuteNonQuery();
}
}
private bool DoesPasswordMatch(string hashedPwdFromDatabase, string userEnteredPassword)
{
return BCrypt.CheckPassword(userEnteredPassword + "^Y8~JJ", hashedPwdFromDatabase);
}
通过这种方式,可以确保即使数据库被窃取,黑客也很难通过暴力破解攻击来获取用户的密码。