在C#应用程序里,直接把用户密码以明文写入数据库是非常危险的做法。一旦数据库被拖库,所有账户都会暴露。正确做法是使用密码哈希加上随机盐值(salt),让相同密码产生不同哈希结果,并大幅度增加暴力破解成本。

为什么需要加盐
单纯使用MD5或SHA256对密码做哈希,攻击者可以用彩虹表反查常见密码。加盐就是在每个用户密码后面拼接一段随机字符串,再计算哈希,这样即使两个用户密码相同,因为盐不同,哈希值也不一样。
常见误区
- 使用固定盐:所有用户用同一个盐,等于没加。
- 自己写哈希循环:容易出安全漏洞,应使用标准算法。
- 用普通SHA256不迭代:计算太快,易被暴力破解。
C#中使用PBKDF2加盐哈希
.NET内置了Rfc2898DeriveBytes类,可以方便地实现PBKDF2算法。下面示例展示如何生成盐并计算哈希:
using System;
using System.Security.Cryptography;
using System.Text;
public class PasswordHasher
{
// 生成随机盐
public static byte[] GenerateSalt(int size = 16)
{
byte[] salt = new byte[size];
using (var rng = RandomNumberGenerator.Create())
{
rng.GetBytes(salt);
}
return salt;
}
// 计算密码哈希
public static string HashPassword(string password, byte[] salt, int iterations = 10000)
{
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, iterations, HashAlgorithmName.SHA256))
{
byte[] hash = pbkdf2.GetBytes(32);
// 将盐和哈希一起转base64方便存储
return Convert.ToBase64String(salt) + ":" + Convert.ToBase64String(hash);
}
}
// 验证密码
public static bool Verify(string password, string stored)
{
string[] parts = stored.Split(':');
byte[] salt = Convert.FromBase64String(parts[0]);
byte[] hash = Convert.FromBase64String(parts[1]);
using (var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 10000, HashAlgorithmName.SHA256))
{
byte[] newHash = pbkdf2.GetBytes(32);
// 比较哈希值
for (int i = 0; i < hash.Length; i++)
{
if (hash[i] != newHash[i]) return false;
}
return true;
}
}
}
数据库中如何存储
通常把盐和哈希拼接成一个字符串(如上例用冒号分隔)存到用户表的一列中,不需要单独建盐字段。验证时取出该值拆分即可。
| 字段 | 说明 |
|---|---|
| id | 用户主键 |
| username | 登录名 |
| pwd_hash | 盐与哈希组合值 |
使用第三方库
如果不想自己封装,也可以使用BCrypt.Net这类库,它内部自动处理盐和迭代次数:
// 安装 BCrypt.Net-Next 后
string hash = BCrypt.Net.BCrypt.HashPassword("用户密码");
bool ok = BCrypt.Net.BCrypt.Verify("用户密码", hash);
总结建议
在C#里安全存储密码,优先使用PBKDF2、bcrypt或Argon2等慢哈希算法,每个用户独立随机盐,迭代次数随硬件提升调整。切勿使用明文或弱哈希,也别把盐当秘密隐藏,正确验证流程才是保护用户的核心。
C#password_hashingsalt修改时间:2026-07-25 23:09:20