<p>BCrypt 是当前更合理的选择,SHA256 不能单独用于密码存储;C# 原生不支持 BCrypt,需引入 BCrypt.Net-Next 等第三方库,而 System.Security.Cryptography 仅提供无盐无迭代的纯 SHA256 实现。</p>

直接说结论:SHA256 不能单独用于密码存储,BCrypt 是当前更合理的选择;但 C# 原生不支持 BCrypt,必须引入第三方库。
C# 的 System.Security.Cryptography 提供了完整的 SHA256 实现,但它只是纯哈希函数——无盐、无迭代、不可调慢,直接用 SHA256.Create() 算密码是危险的。而 BCrypt 天然带盐、可配置工作因子(cost),专为密码哈希设计,但得靠 BCrypt.Net-Next 这类 NuGet 包。
为什么不能直接用 SHA256 存密码
常见错误现象:SHA256.Create().ComputeHash(...) 算出的值在不同环境、不同时间对同一密码永远一致——这正是攻击者最爱的条件。
- 没有自动加盐,相同密码 → 相同哈希 → 可批量撞库
- 计算太快(纳秒级),暴力破解一秒钟能试数百万次
- 不抗彩虹表:网上随便搜“SHA256 rainbow table”,就能查到常用密码的哈希结果
- 数据库泄露后,攻击者几乎零成本还原弱密码(如
123456、password)
SHA256 + Salt 手动实现的坑
有人试图自己拼接盐值再哈希,比如 SHA256.ComputeHash(Encoding.UTF8.GetBytes(salt + password)),这比裸 SHA256 强一点,但仍有硬伤:
- 盐值长度太短(如 8 字节)或生成方式不安全(
new Random().Next())→ 盐可预测 - 没做密钥派生(Key Derivation),缺少迭代轮数 → 无法拖慢暴力尝试速度
- 盐和哈希值怎么存?分开存易丢、一起存格式混乱,验证时还得手动拆解
- 自己写的逻辑难审计,容易漏掉编码一致性(如 UTF8 vs Unicode)、字节序、大小写处理等问题
用 BCrypt.Net-Next 替代原生 SHA256
这是目前 .NET 生态最主流、维护活跃的 BCrypt 实现,已适配 .NET 6+,支持跨平台。
- 安装命令:
dotnet add package BCrypt.Net-Next - 哈希密码:
BCrypt.HashPassword("mypassword", workFactor: 12)→ 返回含 salt 和 cost 的完整字符串,如$2a$12$... - 验证密码:
BCrypt.Verify("input", hashFromDb)→ 自动提取 salt、重算、比对,无需你操心格式 -
workFactor建议设为 10–12:值越大越慢越安全,但登录延迟明显上升(12 ≈ 250ms/次,服务器需权衡)
注意:BCrypt.Net-Next 不兼容老版 BCrypt.Net 的哈希格式,迁移旧数据需重新哈希(用户下次登录时升级)。
如果真要保留 SHA256,至少用 PBKDF2
原生支持、带盐、可迭代,是微软推荐的过渡方案(比裸 SHA256 安全得多,但不如 BCrypt 或 Argon2)。
- 关键类:
Rfc2898DeriveBytes(即 PBKDF2-HMAC-SHA256) - 推荐迭代次数:
100_000起(.NET 6+ 默认是 10000,太低) - 盐长度:
16字节以上,用RandomNumberGenerator.Fill()生成 - 输出长度建议 32 字节(匹配 SHA256 输出),存盐 + 哈希(共 48 字节)
别用 MD5 或 SHA1 做密钥派生——它们已被证实不安全,SHA256 在 PBKDF2 中只是 HMAC 的底层哈希,不是直接暴露的哈希值。
BCrypt 的核心优势不在“算法多新”,而在它把盐、迭代、格式封装成一个原子操作。你自己拼 SHA256 + RNGCryptoServiceProvider + for-loop,哪怕逻辑正确,也极可能在盐存储、编码转换、异常路径上埋下隐患。真正上线前,别省那一个 NuGet 包。


















