必须用 argon2.idkey 而非 argon2.key,因其自动安全生成16字节随机盐并返回标准编码字符串,避免手动控盐导致的盐复用;参数推荐 m=65536、t=3、p=4;验证时应直接用 comparehashandpassword,不可手动解析或比对。

直接用 argon2.IDKey,别碰 argon2.Key —— 后者不带盐自动管理,容易误用导致重复盐或固定盐。
为什么必须用 argon2.IDKey 而不是 argon2.Key
argon2.Key 要求你手动传入盐(salt),但 Go 标准库没提供安全生成盐的辅助函数;很多人随手写 []byte("mysalt123") 或复用同一片内存,结果所有密码哈希都用同一个盐,完全失去抗彩虹表能力。
argon2.IDKey 内部会调用 crypto/rand.Read 生成 16 字节随机盐,并把盐和哈希值拼在一起返回(格式为 $argon2id$v=19$m=65536,t=3,p=4$[salt][hash]),天然防盐复用。
- 盐长度固定为 16 字节,符合 RFC 9106 推荐
- 返回值是完整编码字符串,可直接存数据库,无需额外序列化
- 若你坚持自己控盐(比如要兼容旧系统),才考虑
argon2.Key,但必须确保每次调用都用新crypto/rand.Read
argon2.IDKey 的关键参数怎么设才合理
参数不是越大越好。内存(m)、迭代次数(t)、并行度(p)需在安全性和服务延迟间权衡。Web 登录场景建议起步值:m=65536(64 MiB)、t=3、p=4。
-
m:内存成本,单位字节;65536 = 64 * 1024,对应约 64 MiB 占用;低于 32768 容易被 GPU 暴力加速 -
t:时间成本,即迭代轮数;t=3在普通 CPU 上耗时约 200–400ms,太高会导致登录接口超时 -
p:并行度;设为 CPU 逻辑核数(如 4 或 8),但不要超过服务器核心数,否则反而拖慢并发请求 - 密码长度建议 ≥ 8 字节,
argon2.IDKey对短输入无特殊保护,靠参数硬扛
验证密码时别自己解析哈希字符串
从数据库读出的哈希字符串(如 $argon2id$v=19$m=65536,t=3,p=4$...)直接传给 argon2.CompareHashAndPassword,它会自动提取盐、版本、参数并重算比对。
- 不要用
strings.Split手动切分 —— 版本升级后字段顺序可能变,且 Base64 解码容易出错 - 不要自己调
argon2.IDKey再比对字节 —— 盐和参数必须严格一致,手拼极易出错 -
CompareHashAndPassword内部已做恒定时间比较,防时序攻击,自己写bytes.Equal就破防了
常见错误:哈希值存进数据库前被截断或编码损坏
Argon2 编码后的字符串含 + 和 /,如果走 URL 或 JSON 传输,又没做正确转义,入库时可能被 Web 框架/ORM 自动替换(比如把 + 当空格处理)。
- 存数据库前检查长度:典型
argon2.IDKey输出长度在 100–120 字节之间,低于 90 基本就是被截了 - 避免用
varchar(64)这类过短字段 —— 至少留varchar(255) - 若经 HTTP 传递,用
url.PathEscape或 base64.RawURLEncoding 编码后再传,别依赖默认表单编码
Argon2 的安全性高度依赖盐的随机性与参数的实际执行效果,而不是“用了 Argon2”这个事实。最常被忽略的是:本地开发时用 t=1 测试,上线却忘记改回 t=3,结果生产环境哈希耗时不到 50ms,等于裸奔。











