bcrypt的安全性取决于明文≤72字节、cost=12且数据库字段≥255字符三者缺一不可;明文超长被静默截断,cost非12易致panic或性能问题,哈希存库须转string并校验长度60,比对前需trim、判空及严格参数顺序。

bcrypt.GenerateFromPassword 和 bcrypt.CompareHashAndPassword 本身不提供“高强度”开关——所谓高强度,是靠你严格控制三个硬性条件:明文 ≤ 72 字节、cost = 12、数据库字段 ≥ 255 字符。漏掉任意一条,校验大概率报 invalid hash 或 hashedPassword is not the hash of the given password。
明文密码必须 ≤ 72 字节且非空
Go 的 bcrypt.GenerateFromPassword 对输入 []byte 静默截断:超过 72 字节的部分直接丢弃,不报错也不警告。
- “你好世界?…(含 emoji)” 和它前 72 字节的哈希结果完全一致
- 前端限制可被绕过,服务端必须重校:
if len([]byte(pwd)) == 0 || len([]byte(pwd)) > 72 - 别用
utf8.RuneCountInString(pwd)判长度——它数 rune,不是字节;中文、emoji 全按 UTF-8 字节数算 - 建议前置清洗:
pwd = strings.Map(func(r rune) rune { if unicode.IsControl(r) { return -1 }; return r }, pwd),再 trim 和判空
cost 参数必须显式设为 12 并做范围校验
bcrypt.DefaultCost 当前是 12,但它只是常量,不是运行时自适应值。硬写 10 已不安全,设成 4(测试常用)上线后可能引发 too many open files。
- 永远显式传
bcrypt.DefaultCost,而不是留空或写死数字 - 从配置读取后必须校验:
if cost bcrypt.MaxCost,否则GenerateFromPassword直接 panic - 实测:cost = 12 在主流云服务器单次耗时 80–120ms,QPS 可稳住 50+;cost = 14 可能突破 1s,登录接口易超时
- 测试环境可用 4,但必须与生产配置隔离——否则压测无法暴露真实延迟问题
哈希值存数据库前必须转 string 且字段 ≥ 255 字符
bcrypt.GenerateFromPassword 返回的是 []byte,内容为 ASCII 文本(如 $2b$12$...),固定长度约 60 字节。直接存二进制到数据库,读出来大概率乱码或 decode 失败。
- 正确写法:
hashedStr := string(hashed),再存入数据库 - MySQL 字段至少定义为
VARCHAR(255);VARCHAR(50)必截断,后续所有比对都失败 - PostgreSQL 推荐用
TEXT,避免隐性长度限制 - 存库前建议校验长度:
if len(hashedStr) != 60,立刻报警——说明生成环节异常
CompareHashAndPassword 前必须 trim + 判空 + 检查长度
bcrypt.CompareHashAndPassword 不做容错解析,只认标准格式:以 $2a$ 或 $2b$ 开头、长度恰好 60、校验和有效。任何偏差都会拒绝并报错。
- 查出哈希后,立即
strings.TrimSpace()——空格、BOM、换行都会导致失败 - 调用前必须判空:
if user.Password == "",直接拒绝,不进比对流程 - 检查长度:
if len(user.Password) != 60,说明入库或读取已损坏,需定位源头(ORM 自动 trim?JSON 反序列化?) - 参数顺序不能反:
CompareHashAndPassword([]byte(storedHash), []byte(inputPassword));颠倒必报错
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











