bcrypt比md5或sha-256更适合存储密码,因其是专为密码设计的自适应哈希函数,内置随机盐值、可调工作因子(推荐cost=12),且故意慢以抵御暴力破解;而md5/sha-256计算过快、无内置盐、易受彩虹表攻击,手动加盐也无法实现自适应安全性。

为什么 bcrypt 比 md5 或 sha256 更适合存密码
因为 bcrypt 是专为密码设计的自适应散列函数:它内置盐值、可调计算成本(cost),且故意慢——这能有效拖慢暴力破解速度。而 md5 和 sha256 是通用哈希,快得让攻击者每秒能试数百万次,加盐也挡不住彩虹表批量碰撞。
Go 标准库不提供 bcrypt,得用第三方包:golang.org/x/crypto/bcrypt。别用 crypto/md5 或 crypto/sha256 手动拼接盐再哈希——那不是“自适应”,只是伪安全。
如何正确生成和验证 bcrypt 散列
核心就两个函数:bcrypt.GenerateFromPassword 生成散列,bcrypt.CompareHashAndPassword 验证。关键不是“怎么调用”,而是参数和错误处理是否到位:
-
cost值建议设为12(当前推荐下限),低于10容易被 GPU 破解;高于14可能导致高并发登录超时 - 传给
GenerateFromPassword的原始密码必须是[]byte,不是string—— 虽然 Go 会隐式转换,但显式转更清晰:[]byte(password) -
CompareHashAndPassword对任意输入都返回nil或具体错误,**绝不能用== nil判断成功**,要直接用err != nil分支处理失败
示例片段:
hash, err := bcrypt.GenerateFromPassword([]byte("user123"), 12)
if err != nil {
log.Fatal(err) // 不要忽略
}
// hash 是形如 "$2a$12$..." 的字符串,直接存数据库
err = bcrypt.CompareHashAndPassword(hash, []byte("user123"))
if err != nil {
// 密码错误或 hash 格式非法
}
常见踩坑:盐值哪里来?要不要自己管?
bcrypt 的盐值完全由函数内部生成并嵌入输出哈希字符串中(比如前缀 a$ 后面那串就是),你**不需要、也不应该**自己生成盐、拼接、存储或管理盐值字段。手动维护盐值反而破坏 bcrypt 的设计契约,还容易引入时序攻击风险。
唯一要注意的是:不要截断哈希字符串。完整保存 bcrypt 输出(通常 60 字符左右),MySQL 字段至少设为 VARCHAR(60),PostgreSQL 用 TEXT 更稳妥。如果数据库字段太短,存进去的哈希被截断,后续验证必然失败,且错误信息只是 crypto/bcrypt: hashedPassword is not the correct length,不容易定位。
替换旧密码哈希时要注意什么
如果系统原来用 sha256(password+salt),上线新逻辑后不能一刀切重置所有密码。得在用户下次登录时,先用旧方式验证,成功后再用 bcrypt.GenerateFromPassword 生成新哈希并更新数据库。
迁移过程中注意两点:
- 旧哈希格式要有明确标识(比如数据库加
password_scheme字段,值为"sha256-salt"或"bcrypt") - 验证逻辑要分支判断:
if scheme == "bcrypt" { bcrypt.Compare... } else { legacyVerify(...) }
别在登录接口里写“先试 bcrypt,失败再试旧方式”——这会让攻击者通过响应时间差异推断出用户用的是哪种哈希,构成时序侧信道。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











