bcrypt加密

bcrypt加密
安安Go 中 bcrypt 加密详细讲解
bcrypt 是一种自适应密码哈希算法,由 Niels Provos 和 David Mazières 设计,用于安全存储密码。它基于 Blowfish 加密算法,通过增加计算成本(work factor)来抵抗暴力破解攻击。在 Go 语言中,bcrypt 实现位于 golang.org/x/crypto/bcrypt 包中,这是标准库的扩展包。 它不是对称/非对称加密,而是单向哈希函数,旨在使密码存储安全,无法逆向解密。
1. 安装 bcrypt 包
在 Go 项目中,通过以下命令安装:
1 | go get golang.org/x/crypto/bcrypt |
导入包:
1 | import "golang.org/x/crypto/bcrypt" |
这是一个纯 Go 实现,无需外部依赖。
2. 核心函数和用法
bcrypt 包提供了两个主要函数:GenerateFromPassword 用于生成哈希,CompareHashAndPassword 用于验证。哈希格式为 $2a$cost$salt$hash,其中包含版本、成本、盐和哈希值。
生成哈希(GenerateFromPassword)
- 参数:
password []byte:明文密码(字节切片)。密码长度限制在 72 字节以内,否则返回ErrPasswordTooLong。cost int:计算成本(work factor),范围 4(MinCost)到 31(MaxCost)。默认值为 10(DefaultCost)。成本越高,哈希越慢(指数级增长),推荐根据硬件调整(例如,生产中用 12-14)。
- 返回值:
[]byte:生成的哈希。error:可能的错误如ErrPasswordTooLong或无效成本。
- 内部机制:自动生成随机盐(22 字符),结合密码进行多次迭代哈希。盐防止彩虹表攻击,成本抵抗暴力破解。
代码示例:
1 | package main |
- 生成的哈希每次不同(因随机盐),但验证时一致。
验证哈希(CompareHashAndPassword)
- 参数:
hashedPassword []byte:存储的哈希。password []byte:用户输入的明文密码。
- 返回值:
error:如果匹配,返回 nil;否则返回ErrMismatchedHashAndPassword或其他(如ErrHashTooShort如果哈希太短)。
- 机制:从哈希中提取盐和成本,重新哈希输入密码并比较。时间恒定,防止时序攻击。
代码示例:
1 | package main |
- 注意:始终处理错误,不要用字符串比较哈希。
其他辅助函数
Cost(hashedPassword []byte) (int, error):返回哈希的成本,用于检查是否需要升级哈希(例如,当硬件升级时,提高成本重新哈希旧密码)。- 错误类型:包括
ErrHashTooShort、ErrMismatchedHashAndPassword、ErrPasswordTooLong等。始终检查这些错误以处理无效输入。
3. 成本因子(Cost Factor)和性能
- 成本决定了哈希的迭代次数(2^cost 次)。例如:
- Cost 4:快速(测试用)。
- Cost 10:默认,适合大多数应用(~100ms)。
- Cost 12-14:生产推荐,平衡安全和性能。
- Cost > 20:非常慢,适合高安全需求,但可能导致 DoS 风险。
- 调整建议:基准测试你的硬件,确保哈希时间在 100-500ms。过低易被破解,过高影响用户体验。
- 升级策略:在验证时检查成本,如果低于当前标准,重新哈希并存储。
4. 安全考虑
- 优势:自适应(成本可调)、内置盐、抵抗 GPU/ASIC 破解(内存密集)。
- 限制:密码 > 72 字节不支持(截断或预哈希);不适合通用数据加密,只用于密码。
- 最佳实践:
- 从不存储明文密码。
- 使用 HTTPS 传输密码。
- 结合其他安全措施如 2FA。
- 定期审计:监控暴力攻击,使用 rate limiting。
- 避免自定义实现,使用标准包。
5. 项目中的实际应用
在 Web 项目(如使用 Gin 或 Echo)中:
- 注册:生成哈希存储到数据库(GORM 或 sqlx)。
- 登录:从 DB 获取哈希,验证输入。
示例集成(假设 GORM):
1 | // 注册 |
与 base64 或其他加密/哈希的比较
bcrypt 是密码哈希函数,不是加密或编码。以下比较 base64 和其他常见方法在 Go 中的使用、安全性和适用性。Go 标准库提供 crypto 和 encoding/base64 包。
1. bcrypt vs Base64
Base64:
类型:编码(Encoding),不是加密或哈希。将二进制数据转换为 ASCII 字符串(A-Z、a-z、0-9、+/=)。
Go 实现:
encoding/base64包。用法:
1
2
3import "encoding/base64"
encoded := base64.StdEncoding.EncodeToString([]byte("password")) // 输出 cGFzc3dvcmQ=
decoded, _ := base64.StdEncoding.DecodeString(encoded) // 恢复原数据安全:零安全。可逆转,无保护。用于传输(如 JWT),不是存储密码。
比较:bcrypt 是单向、慢速哈希,设计用于密码;base64 是快速、可逆编码。base64 不提供任何保密,仅改变表示形式。
场景:base64 用于数据传输/存储(如图像);bcrypt 只用于密码哈希。不要用 base64 存储密码,即使结合哈希(bcrypt 已内置 base64 变体编码盐/哈希)。
2. bcrypt vs 其他哈希/加密
**MD5/SHA-1/SHA-256 (crypto/md5, crypto/sha1, crypto/sha256)**:
类型:快速消息摘要哈希。
用法:
1
2
3
4
5
6import (
"crypto/sha256"
"fmt"
)
hash := sha256.Sum256([]byte("password"))
fmt.Printf("%x\n", hash) // 输出 32 字节十六进制安全:快速,易暴力破解;无内置盐。MD5/SHA-1 已废弃(碰撞攻击);SHA-256 更好但不适合密码(太快)。
比较:bcrypt 慢速、自适应、带盐;SHA 等快速,用于校验文件/数据完整性,不是密码。bcrypt 更安全,但计算开销大。
场景:SHA 用于签名/校验;bcrypt 用于密码。
**Argon2 (golang.org/x/crypto/argon2)**:
类型:内存硬化哈希,OWASP 推荐。
用法:类似 bcrypt,但参数包括时间、内存、线程。
1
2
3import "golang.org/x/crypto/argon2"
salt := []byte("somesalt")
hash := argon2.IDKey([]byte("password"), salt, 1, 64*1024, 4, 32)安全:抵抗侧信道攻击,内存/CPU 可调。更现代,比 bcrypt 灵活。
比较:Argon2 更可配置(内存硬化防 GPU 攻击);bcrypt 简单、成熟。Argon2 可能取代 bcrypt,但 bcrypt 仍广泛使用。
场景:高安全需求用 Argon2;简单应用用 bcrypt。
**scrypt (golang.org/x/crypto/scrypt)**:
类型:内存硬化密钥派生函数。
用法:
1
2
3import "golang.org/x/crypto/scrypt"
salt := []byte("somesalt")
dk, _ := scrypt.Key([]byte("password"), salt, 16384, 8, 1, 32)安全:类似 Argon2,防暴力。
比较:scrypt 内存密集;bcrypt CPU 密集。scrypt 更灵活,但参数复杂。
场景:替代 bcrypt 时使用。
**AES/RSA 等加密 (crypto/aes, crypto/rsa)**:
类型:对称/非对称加密,可逆。
用法:AES 用于数据加密。
1
2import "crypto/aes"
// 复杂,需要密钥、IV 等安全:可解密;用于数据保护,不是密码存储。
比较:加密是双向;bcrypt/哈希单向。密码应哈希,不加密(OWASP 推荐)。
场景:AES 用于文件加密;bcrypt 用于认证。
总结比较:bcrypt 是密码哈希首选(安全、易用);base64 仅编码;快速哈希如 MD5 不安全;Argon2/scrypt 是现代替代。选择取决于需求:密码用慢哈希,数据用加密,传输用编码。

