不能用md5或sha1做密码相关操作,因二者计算极快、易被暴力破解;sha256是当前事实标准,适用于签名、证书、区块链及配合pbkdf2的密码派生。

crypto/md5、sha1、sha256 三者怎么选?
别用 md5 或 sha1 做密码相关操作 —— 它们输出固定、计算极快,GPU 每秒能爆破数亿次。真实场景里,md5 只适合校验小文件完整性或生成缓存 key;sha1 已被 NIST 官方弃用,连 Git 都在逐步淘汰它;sha256 是当前事实标准,输出 32 字节(64 位 hex),可用于签名、证书、区块链、密码派生(配合 PBKDF2)等。
常见错误现象:md5.Sum() 返回的是 [16]byte,直接 fmt.Printf("%x", hash) 没问题,但若误用 hash[:] == otherHash[:] 比较,要注意切片底层数组是否被复用导致意外相等。
-
sha256.Sum256([]byte("x"))返回[32]byte,比sha256.New().Write().Sum(nil)更省内存,适合短数据 - 处理大文件时必须用
sha256.New()+io.Copy()或分块Write(),避免一次性加载全部内容到内存 - 所有哈希函数都不带 salt —— salt 是业务逻辑层的事,不是哈希函数本身的责任
加盐不是把 password + salt 拼字符串那么简单
真正安全的加盐,核心是两个不可妥协的条件:盐值必须密码学随机、且每次哈希独立生成。用 time.Now().Unix()、用户名、或固定字符串当 salt,等于没加;用 strings.Join([]string{pwd, salt}, "") 拼接,一旦 salt 含 \x00 或非 UTF-8 字节,就可能被截断或解析失败。
正确做法只有一条:用 crypto/rand.Read() 生成 salt,例如 salt := make([]byte, 16) 后调用 rand.Read(salt)。之后再决定拼接顺序 —— 推荐 salt + password(而非反序),因为多数标准如 PBKDF2、scrypt 默认如此,且能规避某些边界解析问题。
- salt 长度建议 16 字节起,太短易被彩虹表覆盖;太长(如 64 字节)无实际增益,还浪费存储
- 必须把 salt 和最终 hash 一起持久化,且字段长度足够(比如 bcrypt 哈希需 ≥60 字符,VARCHAR(255) 更稳妥)
- 数据库字段若设为
VARCHAR(50),bcrypt 哈希一存就截断,验证时bcrypt.CompareHashAndPassword直接报invalid hash,不是密码错,是 hash 损坏
为什么不能用 MD5 + salt 存密码?
因为 md5 本身没有成本参数,加了 salt 也挡不住暴力穷举。攻击者拿到数据库后,对每个 salt + password 组合做离线爆破,现代显卡每秒跑几亿次 md5,一个 8 位字母数字密码平均 2 分钟就能破解。这不是理论风险,是已公开的实战手法。
Go 生态里唯一推荐的密码哈希方案是 golang.org/x/crypto/bcrypt —— 它自动完成 salt 生成、cost 控制、结果编码三件事,返回字符串形如 $2a$12$...,验证时直接传原密码和该字符串即可,完全不用手动拼接或解析。
-
bcrypt.GenerateFromPassword([]byte(pwd), 12)中 cost=12 是当前平衡点:单次耗时约 80–120ms,P95 登录延迟可控 - cost 必须显式传入,不传会退化到默认 10,而 cost=10 在 2026 年已无法抵御中等算力攻击
- cost 超出 4–31 范围会 panic,务必在配置加载后立即校验:
if cost 31
crypto/pbkdf2 是什么场景下该用的?
当你需要从密码派生密钥(比如加密 AES 密钥)、或对接遗留系统要求 PBKDF2 格式时才用它。它不像 bcrypt 那样开箱即用,必须自己管理 salt、迭代次数、哈希函数选择 —— 错一步就降级成裸哈希。
关键参数只有三个:pbkdf2.Key([]byte(password), salt, iter, keyLen, sha256.New)。其中 iter 至少设为 100000(NIST 建议),salt 必须用 crypto/rand 生成,sha256.New 是函数值,不是字符串。
- 别用
sha1或md5当哈希函数 —— 它们已被证明不抗碰撞,PBKDF2 的安全性取决于底层哈希 - keyLen 通常设为 32(对应 AES-256)或 16(AES-128),设太大无意义,还拖慢性能
- 迭代次数翻倍,耗时几乎翻倍,压测时注意 CPU 使用率突增可能引发容器 OOM 或连接堆积
真正难的不是调用函数,而是确保 salt 独立存储、迭代数不写死在代码里、哈希函数不随时间降级 —— 这些细节漏掉一个,整个链条就垮了。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











