c# - 如何将用户提供的登录凭据与存储在数据库中的盐和哈希进行比较以允许用户登录
问题描述
我在网上做了一些研究,看了一些 YouTube 视频,拼凑了一个代码来加盐密码,然后散列加盐密码,并将加盐和散列存储到数据库中。现在,我目前正纠结于如何将用户提供的密码与存储在数据库中的 salt 进行比较。
我不知道从哪里开始,我真的可以使用一些指导和帮助。虽然我确实尝试过手动取盐并将其与 verifyPassword 进行比较。但它不起作用。我相信我对代码缺乏一些理解。
用户注册页面
public class HashSalt
{
public string Hash { get; set; }
public string Salt { get; set; }
}
public static HashSalt GenerateSaltedHash(int size, string password)
{
var saltBytes = new byte[size];
var provider = new RNGCryptoServiceProvider();
provider.GetNonZeroBytes(saltBytes);
var salt = Convert.ToBase64String(saltBytes);
var rfc2898DeriveBytes = new Rfc2898DeriveBytes(password, saltBytes, 10000);
var hashPassword = Convert.ToBase64String(rfc2898DeriveBytes.GetBytes(256));
HashSalt hashSalt = new HashSalt { Hash = hashPassword, Salt = salt };
return hashSalt;
}
protected void Button1_Click(object sender, EventArgs e)
{
HashSalt hashSalt = GenerateSaltedHash(64, txtPassConfirm.Text);
try
{
SqlConnection con = new SqlConnection(ConfigurationManager.ConnectionStrings["DataBase"].ConnectionString);
con.Open();
SqlCommand cmd = new SqlCommand("insert into Customer values (@FirstName, @LastName, @Nationality, @Email, @Tel);" +
"insert into Account values(@Email2, @Salt, @Hash);", con);
cmd.Parameters.AddWithValue("FirstName", txtFName.Text);
cmd.Parameters.AddWithValue("LastName", txtLName.Text);
cmd.Parameters.AddWithValue("Nationality", ddlNationality.SelectedItem.Text);
cmd.Parameters.AddWithValue("Email", txtMail.Text);
cmd.Parameters.AddWithValue("Tel", txtTel.Text);
cmd.Parameters.AddWithValue("Email2", txtMail.Text);
cmd.Parameters.AddWithValue("@salt", hashSalt.Salt);
cmd.Parameters.AddWithValue("@hash", hashSalt.Hash);
cmd.ExecuteNonQuery();
con.Close();
}
catch (SqlException ex)
{
if (ex.Number == 2627)
{
}
else
{
throw;
}
}
txtFName.Text = "";
txtLName.Text = "";
ddlNationality.SelectedItem.Text = "";
txtTel.Text = "";
txtMail.Text = "";
Response.Redirect("Login.aspx");
}
用户登录页面
public static bool VerifyPassword(string enteredPassword, string storedHash, string storedSalt)
{
var saltBytes = Convert.FromBase64String(storedSalt);
var rfc2898DeriveBytes = new Rfc2898DeriveBytes(enteredPassword, saltBytes, 10000);
return Convert.ToBase64String(rfc2898DeriveBytes.GetBytes(256)) == storedHash;
}
protected void btnLogin_Click1(object sender, EventArgs e)
{
string ConnectionString = ConfigurationManager.ConnectionStrings["DataBase"].ConnectionString;
SqlConnection myConnect = new SqlConnection(ConnectionString);
string CommandText = "SELECT FirstName from Customer" + "SELECT Email, Salt, Hash from Account";
SqlCommand cmd = new SqlCommand(CommandText, myConnect);
myConnect.Open();
SqlDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
if (reader["Email"].ToString() == txtMail.Text )
{
bool isPasswordMatched = VerifyPassword(txtPass.Text,)
Session["User"] = reader["FirstName"].ToString();
}
else
{
lblResult.Text = "Incorrect Username or Password, Please Try Again!";
}
}
}
数据库表
CREATE TABLE [dbo].[Account] (
[EMail] NVARCHAR (50) NOT NULL,
[Salt] NVARCHAR (MAX) NOT NULL,
[Hash] NVARCHAR (MAX) NOT NULL,
PRIMARY KEY CLUSTERED ([EMail] ASC)
);
解决方案
如果您愿意在散列存储中使用 Salt,则应将密码输入的盐值保留在数据库中,附加到散列值或存储在另一列中,就像您这样做。
要验证密码,您的代码需要查询给定用户的盐值,并在哈希摘要方法中使用该值,就像您在密码盐 + 哈希计算中使用的一样,然后进行比较。
总结一下:
- 为新密码存储请求生成随机盐值;
- 定义您要附加盐值的位置。您可以进行连接,例如 salt+password、password+salt。重要的是生成包含作为输入的盐和密码的哈希摘要,以使彩虹表的使用更加困难;
- 存储使用密码和盐生成的哈希,并存储盐;
- 当您的用户尝试登录时,在您的数据库中获取该用户的盐值,然后根据他的输入应用相同的散列方法,将其与您查询的盐连接起来,就像您的散列存储方法一样;
- 如果两个哈希摘要相等,则可以继续进行身份验证。
看看这篇文章,以更好地理解加盐。
推荐阅读
- wordpress - 按功能更改 xml 自定义提要后出错
- java - 在java中将不同的对象添加到列表中
- java - Java / Jackson - 'Unrecognized token' 传递 JSON 对象参数
- javascript - 如何使用 NodeJS 应用程序文件动态地将 CSS 和 JS 排入页面
- scala - 当 JOIN 列不同时,使用 Spark Scala 动态连接数据帧
- javascript - 需要 Javascript 新运算符帮助 - 在 javascript 中使用新运算符的动态对象
- mysql - 创建表时 MySQL 十进制语法错误
- android - 如何实现相同的转换?
- java - 每个模块(在多模块 Maven 项目中)是否应该有自己的专用异常?
- sql - 使用 SQL Server 将多个 Like 行合并为一行