密码哈希必须用bcrypt或argon2,禁用md5/sha256——因二者无盐、计算过快、易被暴力破解;go标准库不提供安全密码哈希,唯一生产推荐是golang.org/x/crypto/bcrypt(简单可靠)或argon2(内存抗性强但配置复杂)。

密码哈希必须用 bcrypt 或 argon2,别碰 md5/sha256
Go 标准库不提供安全的密码哈希函数,crypto/md5 和 crypto/sha256 不能直接用于密码——它们太快、无盐、易被暴力破解。生产环境唯一合理选择是 golang.org/x/crypto/bcrypt(简单可靠)或 golang.org/x/crypto/argon2(内存抗性更强,但配置更复杂)。
常见错误:用 sha256.Sum256() 加个随机字符串当“盐”就存库——这仍是可并行爆破的弱防护。
-
bcrypt默认成本因子为 10,建议至少设为 12(bcrypt.DefaultCost是 10,不够安全) - 每次调用
bcrypt.GenerateFromPassword()都会生成新盐,无需手动管理 - 哈希结果本身已包含盐和参数,长度固定(60 字符),直接存字符串即可
用 bcrypt.GenerateFromPassword 正确生成哈希
不要自己拼接盐、不要重复哈希、不要截断输出。Go 的 bcrypt 包已封装全部细节,只需传入原始密码和成本因子。
hash, err := bcrypt.GenerateFromPassword([]byte("user123!"), 12)
if err != nil {
log.Fatal(err)
}
// hash 是 []byte,转 string 存数据库:string(hash)
// 示例输出:$2a$12$ZzFmYmJlYzE5MjQwNGUyY2RlZmYxZjIzYzQ1NjczZDk1MjE1ZjQ1Ng==
注意:GenerateFromPassword 返回的是完整 bcrypt 哈希字符串(含算法标识、成本、盐、密文),不是 raw bytes;验证时直接用这个字符串,别 Base64 解码或截取。
- 成本因子 12–14 是当前平衡安全与响应时间的合理范围(>14 可能导致登录延迟明显)
- 输入密码前不必 trim 空格——但业务层应做合理性校验(如最小长度、禁止纯数字)
- 若遇到
bcrypt: password too long错误,说明超过 72 字节(UTF-8 下约 72 个 ASCII 字符),需前端限制或截断(不推荐截断敏感字段)
用 bcrypt.CompareHashAndPassword 验证而非自实现比对
永远不要用 == 或 bytes.Equal 比较哈希结果——这会引入时序攻击风险。必须使用 bcrypt.CompareHashAndPassword,它内部做了恒定时间比较。
Go 配置库,使用 spf13/viper — 分层优先级(flag > env >file > KV > default),提供 BindPFlag/BindPFlags、SetEnvPrefix + SetEnvKeyReplace 等功能。
err := bcrypt.CompareHashAndPassword([]byte(storedHash), []byte(inputPassword))
if err != nil {
// err == bcrypt.ErrMismatchedHashAndPassword 表示密码错
// 其他 err(如解码失败)才需记录告警
return false
}
常见坑:
- 传给
CompareHashAndPassword的第一个参数必须是原始存储的哈希字符串(string转[]byte即可),不是从数据库取出后又 base64 decode 过的 - 如果数据库字段类型是
CHAR(60)或VARCHAR(60),确保没被右填充空格(MySQL 的CHAR类型会补空格) - 验证失败时统一返回 “用户名或密码错误”,别区分“用户不存在”和“密码错误”,防止用户名枚举
环境变量里不放明文密码,也不该存加密密钥
哈希是单向的,不需要解密——所以根本不存在“密码加密”这回事。所谓“加密存储密码”本身就是危险误导。真正要保护的是:数据库连接凭据、API 密钥、JWT 签名密钥等可逆敏感项。
这类密钥若必须硬编码,应使用操作系统级隔离(如 Linux 的 secrets、Kubernetes Secret),而非 Go 代码里写死或塞进 .env 文件。
-
os.Getenv("DB_PASSWORD")依然属于明文暴露——进程列表、日志、调试器都可能泄露 - 不要用
AES-GCM自己加密配置项——密钥怎么存?又回到原点 - 真正安全的做法:启动时由外部注入(如 vault agent 注入文件),或通过 runtime 绑定(如 AWS IAM roles for service accounts)
密码哈希本身不依赖密钥,bcrypt 输出天然可独立验证;所有试图“再加密一遍哈希”的操作,只会增加故障面,不提升安全性。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










