bcrypt是go中最稳妥的密码哈希选择,其generatefrompassword和comparehashandpassword已封装salt生成、cost控制与编码,输出为自包含字符串(含算法标识、cost、salt、hash),须直接存储全长约60字符的哈希值并用comparehashandpassword校验,禁止手动拆分salt或自行比较。

用 bcrypt 封装密码哈希,别自己拼 salt
bcrypt 是 Go 中最稳妥的密码哈希选择,它把 salt 生成、成本控制、哈希编码全包进 GenerateFromPassword 和 CompareHashAndPassword 两个函数里。你不需要手动管理 salt 字符串或 base64 编码逻辑。
常见错误是试图“优化”:比如提前生成 salt 再传给 bcrypt,或者把哈希结果拆开存 salt 和 hash 两字段——bcrypt 的输出本身就是自包含的(含算法标识、cost、salt、hash),硬拆只会引入校验失败或兼容性断裂。
-
GenerateFromPassword默认使用bcrypt.DefaultCost(目前为 12),对大多数 Web 应用足够安全;若需调优,可设为 10–14 之间,但别低于 10 - 存储时直接保存整个
string(hashed),长度约 60 字符,MySQL 用VARCHAR(255)完全够用 - 校验必须用
CompareHashAndPassword,别用==或subtle.ConstantTimeCompare自己比——前者不防时序攻击,后者需要字节切片且易出错
封装 PBKDF2 时,参数必须显式固化
PBKDF2 比 bcrypt 更难封装干净,因为它的安全性严重依赖三个参数:迭代次数、盐长、密钥长度。这些值一旦写死在代码里,未来升级就容易出兼容问题。
典型坑是把 PBKDF2Iterations 硬编码成 100000,然后几年后想升到 600000,老用户登录就失败——因为新旧哈希无法共存校验。
- 建议在哈希字符串前加版本前缀,如
v2$,解码时先识别版本再分发到对应解码逻辑 - 盐长和密钥长度必须与迭代次数配套:例如
32字节密钥 +16字节 salt +300000迭代是合理组合;混用12字节 salt 和32字节 key 会导致pbkdf2.Key返回错误 - 别用
sha1作 PRF;Go 标准库默认是sha256,显式传参时务必写sha256.New,否则低版本 Go 可能 fallback 到不安全的sha1
避免在封装层暴露 crypto/aes 底层细节
如果你真要封装对称加密(比如加密用户私有字段),别让用户操心 IV、填充、block size。AES-CBC 手动实现极易出错:IV 不随机、填充没补满、密钥长度不对、解密时忘了截掉 IV —— 任一环节都会让 Decrypt panic 或返回乱码。
更糟的是,有人把 AES 封装成 “encryptString(key, text) → string”,结果 key 传 []byte("my-key")(6 字节),直接触发 aes.NewCipher: invalid key size。
- 强制要求密钥为
32字节(AES-256),并在构造器里用panic或error检查;别尝试自动 padding 密钥 - 用
cipher.AEAD(如cipher.NewGCM)替代裸 CBC:它把 IV(nonce)、认证标签、加密/解密全包进Seal和Open,且自动防篡改 - nonce 长度不能错:GCM 要
12字节,ChaCha20-Poly1305 要24字节;传错长度会静默失败或 panic
签名与验签逻辑必须分离密钥生命周期
RSA 或 ECDSA 签名封装最容易被忽略的点,是私钥加载方式。很多封装把 private.pem 路径写死在结构体里,或者每次签名都重新读文件+解析 PEM——这既慢又不安全(私钥可能被多次加载进内存)。
另一个问题是公钥复用:验签时若每次都用 x509.ParsePKIXPublicKey 解析 PEM,性能损耗明显,尤其在高并发 API 场景。
- 私钥应在应用启动时一次性加载、解析、缓存为
*rsa.PrivateKey,别暴露原始 PEM 字节 - 公钥同理,解析后缓存为
crypto.PublicKey接口,验签时直接复用 - 签名函数签名应为
Sign(payload []byte) ([]byte, error),别塞进任何路径、密码、格式参数——那些属于初始化阶段的事
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











