3.1 KiB
3.1 KiB
官方扩展库 bcrypt
bcrypt 是什么
bcrypt 是专门用于密码哈希的算法,不是可逆加密。
password
↓
bcrypt
↓
hash
核心原则
数据库只存 hash
bcrypt 自动处理 salt
使用 CompareHashAndPassword 验证
cost 根据服务器性能决定
不要自己用 MD5 / SHA256 存密码
cost
cost 是 bcrypt 的计算难度。
生产环境通常可以从 10 ~ 12 开始测试,具体值应根据服务器性能决定。单次 bcrypt 计算控制在大约 100ms ~ 300ms 是比较常见的工程范围。
cost 每增加 1,计算量大约接近翻倍。
cost 越高:
- 计算越慢
- 暴力破解成本越高
- 服务器 CPU 消耗也越高
bcrypt 生成的 hash 本身会记录当时使用的 cost。
因此,即使生产环境将当前配置从:
cost = 10
升级为:
cost = 12
数据库中原本使用 cost = 10 生成的旧 hash,仍然可以通过:
bcrypt.CompareHashAndPassword(...)
正常验证。
CompareHashAndPassword 会根据已有 hash 中记录的信息进行密码验证,不依赖当前系统配置的 cost。
登录成功后,可以通过 bcrypt.Cost() 获取旧 hash 使用的 cost。如果低于当前配置,可以重新生成 hash 并更新数据库。
72-byte 限制
bcrypt 密码输入最多支持约 72 bytes。
如果只允许 ASCII:
72 bytes ≈ 72 个字符
通常已经足够。
建议业务层主动限制:
if len([]byte(password)) > 72 {
return errors.New("password too long")
}
基础使用
依赖导入
import "golang.org/x/crypto/bcrypt"
golang.org/x/crypto/bcrypt 属于 Go 官方维护的扩展库,但不属于 Go 标准库。
生成 hash
hash, err := bcrypt.GenerateFromPassword(
[]byte(password),
12,
)
生成的 hash 类似:
$2a$12$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
其中大致包含:
$2a$ → bcrypt 版本
12$ → cost
后续内容 → 编码后的 salt + hash
bcrypt 会自动生成随机 salt,因此不需要自己生成或额外保存 salt。
验证密码
err := bcrypt.CompareHashAndPassword(
[]byte(passwordHash),
[]byte(password),
)
if err == nil {
// 密码正确
}
判断密码是否相同
不能直接比较两个 bcrypt hash,因为 bcrypt 每次都会生成新的随机 salt。
即使密码完全相同:
hash1 != hash2
判断某个密码是否和旧密码相同时,应使用:
err := bcrypt.CompareHashAndPassword(
[]byte(oldHash),
[]byte(password),
)
if err == nil {
// password 与 oldHash 对应的密码相同
}
获取 hash 的 cost
cost, err := bcrypt.Cost(
[]byte(user.PasswordHash),
)
通常用于用户登录成功后,判断当前密码 hash 是否使用了旧的 cost。
例如:
数据库 hash cost = 10
当前系统 cost = 12
↓
登录成功
↓
重新生成 cost = 12 的 hash
↓
更新数据库
这样可以在用户正常登录过程中逐步升级旧密码 hash。