bcrypt是不可逆的密码哈希而非加密,必须用GenerateFromPassword生成带盐哈希、CompareHashAndPassword验证,禁用AES等可逆加密;cost建议12左右平衡安全与性能;哈希值需存为VARCHAR(255)并端到端校验。

bcrypt 不是“加密”,而是不可逆的密码哈希;用 bcrypt.GenerateFromPassword 生成带盐哈希值,用 bcrypt.CompareHashAndPassword 验证——别试图“解密”它。
为什么必须用 bcrypt 而不是 AES 加密密码
密码存储场景下,“可逆”本身就是严重设计错误。AES 是对称加密,一旦密钥泄露,所有密码瞬间裸奔;而 bcrypt 是专为密码设计的慢哈希函数,自带盐值、抗彩虹表、防暴力穷举。
- 每次调用
bcrypt.GenerateFromPassword输出都不同,哪怕明文相同——因为内部自动生成随机盐 - 哈希结果字符串(如
$2a$10$...)本身已编码了版本、成本因子、盐和密文,无需额外存盐字段 - 若误用
crypto/aes“加密密码”,等于把锁芯和钥匙一起焊死在门上,运维或审计时根本没法安全轮换密钥
bcrypt.GenerateFromPassword 的 cost 参数怎么选
cost 决定计算强度:值越大,哈希越慢,爆破越难,但登录延迟也越高。这不是越“高”越好,而是要平衡安全与体验。
- 默认值
bcrypt.DefaultCost是 10,适合大多数 Web 应用(约 10–15ms/次) - 生产环境建议压测后定值:从 12 开始(约 40–60ms),若用户量大、API 延迟敏感,可降回 11;若为后台管理类系统且硬件较强,可试 13
- 千万别用
time.Now().Unix()或固定数字硬编码 cost——它必须是常量整数,且应随部署环境统一配置
验证失败时 bcrypt.CompareHashAndPassword 返回什么错误
它只返回两种可能:nil(匹配成功)或具体错误。最常见的是 crypto/bcrypt: hashedPassword is not the hash of the given password,但你**绝不能**直接把这个错误返回给前端。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 所有密码验证错误,对外统一返回泛化提示(如 “用户名或密码错误”),防止撞库攻击者探测账号是否存在
- 日志中可记录完整错误,但需脱敏:去掉明文密码,只记哈希前缀和请求 ID
- 注意:传入的哈希值必须是完整字符串(含
$2a$...前缀),若被截断或 base64 解码错位,会静默返回不匹配,而非 panic
哈希值存在数据库里要注意什么
bcrypt 输出是 ASCII 字符串,长度固定约 60 字节,但不同版本略有浮动($2b$, $2y$ 等)。字段设计稍不留神就会被截断。
- 数据库字段类型必须是
VARCHAR(255)或更长的TEXT,别用VARCHAR(60)——未来升级算法可能变长 - 写入前不做任何 trim / replace / url.QueryEscape,保持原始字节流
- 查询时务必用参数化语句,避免拼接 SQL——否则哈希值里的
$符号可能触发模板引擎提前解析
真正容易被忽略的点是:bcrypt 哈希值本身是“自包含”的,但它不提供任何完整性校验机制。如果你在传输或落库过程中意外损坏了几个字节(比如 UTF-8 BOM、换行截断、gzip 二次压缩),验证会静默失败,且无法区分是攻击还是故障。所以,上线前一定要用真实哈希值跑一遍端到端读写校验。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










