Go crypto包安全但需正确使用:AES需用AEAD防攻击,密钥等必须用crypto/rand生成,密码哈希应选bcrypt而非SHA256,TLS配置须显式设MinVersion、CurvePreferences和CipherSuites。

Go 标准库的 crypto 包是安全的,但“安全”不等于“开箱即用就防一切风险”——它提供的是经过严格审计的原语(primitives),不是自动帮你规避所有误用的魔法工具。
为什么 crypto/aes 本身安全,但你一写错就可能被绕过?
Go 的 AES 实现基于硬件加速(如 AES-NI)且通过了 NIST 测试,但标准库只暴露底层块加密模式(如 aes.NewCipher),不封装认证加密(AEAD)。这意味着:
- 直接用
cbc.Encrypt+ 自己拼接 IV 和密文,没有完整性校验,容易遭受填充预言攻击(padding oracle) -
ECB模式在crypto/cipher中仍存在,但完全不推荐——相同明文块永远生成相同密文块,图像加密后还能看出轮廓 - IV 必须随机且不可预测,但标准库不帮你生成;若复用 IV(尤其在
CBC或CTR中),密文可被异或分析
✅ 正确做法:优先用 crypto/cipher.AEAD 接口,例如 cipher.NewGCM,它强制绑定 nonce、密文和认证标签(tag),解密失败时直接返回 error,不会泄露中间状态。
crypto/rand 和 math/rand 混用会出什么问题?
这是最常踩的坑:math/rand 是伪随机数生成器(PRNG),种子固定就能复现全部输出;而加密场景(如生成密钥、nonce、salt)必须用密码学安全的随机源。
立即学习“go语言免费学习笔记(深入)”;
- 用
math/rand.Int()生成 AES 密钥?密钥空间实际只有 2³² 量级,暴力几秒就破 -
crypto/rand.Read(buf)可能返回io.ErrUnexpectedEOF,必须检查错误,不能忽略 - Windows 下
crypto/rand读取\\.\Device\Crypto\Random,Linux 下读/dev/urandom,行为一致且足够安全
✅ 示例:生成 32 字节密钥
key := make([]byte, 32)
if _, err := rand.Read(key); err != nil {
log.Fatal(err) // 不要忽略
}
SHA256 和 bcrypt 该选哪个做密码哈希?
crypto/sha256 是通用哈希函数,设计目标是抗碰撞、不可逆,但**不适合直接哈希密码**:
- 计算太快:GPU 一秒可试数十亿次 SHA256,彩虹表+盐值也挡不住弱口令
- 没有内置 salt 管理、迭代次数控制或内存硬化机制
-
bcrypt、scrypt、argon2才是专为密码设计的 KDF(密钥派生函数)
✅ Go 官方推荐用 golang.org/x/crypto/bcrypt(非标准库,但由 Go 团队维护):
hash, _ := bcrypt.GenerateFromPassword([]byte("mypass"), bcrypt.DefaultCost)
// 验证时用 bcrypt.CompareHashAndPassword(hash, []byte("mypass"))
注意:DefaultCost 当前是 10,对应约 2¹⁰ 次哈希迭代;生产环境建议根据服务器性能调到 12–14。
HTTPS 服务中 crypto/tls 的常见配置陷阱
Go 的 tls.Config 默认启用 TLS 1.2+,但几个关键字段不设默认值,留空就等于放行不安全行为:
-
MinVersion不设 → 可能协商到 TLS 1.0(已废弃) -
CipherSuites不设 → 使用 Go 默认列表,包含部分弱套件(如带 RC4 或 CBC 的) -
CurvePreferences不设 → 服务端可能选低效曲线(如CurveP224),或不支持现代椭圆曲线(如X25519)
✅ 生产建议显式配置:
config := &tls.Config{
MinVersion: tls.VersionTLS12,
CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256},
CipherSuites: []uint16{
tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
// 排除所有 CBC 和 SHA1 套件
},
}
真正难的从来不是调用哪个函数,而是理解每个参数背后放弃的兼容性、接受的性能代价、以及没写进文档的隐含假设。










