应显式指定 bcrypt cost 为 12 或 14,用 CompareHashAndPassword 恒定时间比对,并加限流熔断防 CPU 过载;存储字段需 VARCHAR(255) + utf8mb4_bin,密码须统一编码和去空格。
为什么不能直接用
存密码?
因为默认参数太弱,
的第二个参数是 cost 值,传
看似合理,但 Go 官方 bcrypt 实现里,cost 低于
会 panic,高于
会被截断;而生产环境建议至少用
(2024 年主流云服务器上耗时约 200ms/次),否则容易被暴力破解。
常见错误现象:
或哈希值长度异常(少于 60 字符),本质是 cost 越界或传了负数。
永远显式传 cost,别依赖 magic number 或配置文件未校验的值
cost 建议固定为
或
,不要动态计算——CPU 差异会导致测试环境快、线上慢,进而拖垮登录接口
如果用
,确保版本 ≥ v0.17.0,旧版对高 cost 支持不稳
怎么安全比对用户登录时提交的密码?
必须用
,而不是自己解哈希再比较。bcrypt 哈希字符串自带 salt 和 cost,
内部会自动提取并重算,且做了恒定时间比较(timing-safe),避免时序攻击。
常见错误现象:手写
,不仅不安全,还会因 base64 编码差异(比如换行符)导致误判;或者把
的返回值当成布尔值直接取反使用。
立即学习
“
go语言免费学习笔记(深入)
”;
输入参数顺序不能反:
返回
表示匹配成功,
表示失败,其他 error 需记录并告警(比如哈希格式损坏)
不要在 error 判断里用
——error message 可能变,应直接与
比较
如何避免
在高并发下拖垮服务?
bcrypt 是 CPU 密集型操作,单次调用会阻塞 goroutine。如果每秒处理上百个登录请求,且没做限流或异步化,很容易吃光 P 值(GOMAXPROCS),让整个服务响应变慢甚至超时。
go语言参考手册 中文CHM版
Go 是一个开源的编程语言,它能让构造简单、可靠且高效的软件变得容易。本文给大家带来Go参考手册,需要的可以来下载! Go是从2007年末由Robert Griesemer, Rob Pike, Ken Thompson主持开发,后来还加入了Ian Lance Taylor, Russ Cox等人,并最终于2009年11月开源,在2012年早些时候发布了Go 1稳定版本。现在Go的开发已经是完全开放的,并且拥有一个活跃的社区。 Go 语言特色 简洁、快速、安全 并行、有趣、开源 内存管理、v数组安全、编译
下载
性能影响明显:cost=12 时,单核每秒最多处理 3–5 次完整校验;cost=14 就掉到 1–2 次。
登录接口必须加熔断和限流(比如用
+
)
不要在中间件或 handler 里直接调
——先校验用户名是否存在、账号是否锁定,再进 bcrypt
绝对不要把 bcrypt 放进数据库事务里(尤其 PostgreSQL 的 serializable mode),会延长锁持有时间
还有哪些容易被忽略的细节?
bcrypt 哈希值是 ASCII 字符串,但 Go 的
默认按 UTF-8 解释;如果用户密码含非 ASCII 字符(比如中文、emoji),必须确保前后编码一致,否则比对必然失败。
另一个坑是:有人把哈希结果存进 MySQL
,但实际 bcrypt 输出最长可达 60 字符,某些版本驱动或 collation 下会静默截断最后 1–2 位,导致永远无法登录。
存储字段至少定义为
,MySQL 推荐用
collation
密码输入前统一用
,防止前端多传空格或换行
调试时打印哈希值,务必用
而不是
,避免不可见字符干扰判断
bcrypt 不是银弹,它只解决“存储侧防明文泄露”这一个问题。传输层仍需 HTTPS,登录态要用 secure+httpOnly cookie 或短时效 JWT,这些环节漏一个,前面全白做。
bcrypt.GenerateFromPasswordbcrypt.GenerateFromPassword1043112invalid bcrypt cost: 31214golang.org/x/crypto/bcryptbcrypt.CompareHashAndPasswordCompareHashAndPasswordif hash == storedHashCompareHashAndPasswordbcrypt.CompareHashAndPassword([]byte(storedHash), []byte(inputPassword))nilbcrypt.ErrMismatchedHashAndPasswordstrings.Contains(err.Error(), "mismatch")bcrypt.ErrMismatchedHashAndPasswordbcryptgobreakergolang.org/x/time/rateCompareHashAndPassword[]byteVARCHAR(60)VARCHAR(255)utf8mb4_binbytes.TrimSpacefmt.Printf("%q", hash)fmt.Println(hash)