108 lines
3.3 KiB
Markdown
108 lines
3.3 KiB
Markdown
---
|
||
tags: [go, security, bcrypt, password-hashing, cost-factor]
|
||
create time: 2026-04-25 14:45
|
||
---
|
||
|
||
# bcrypt 成本因子(Cost Factor)详解
|
||
|
||
## 概述
|
||
|
||
成本因子是 bcrypt 算法的核心参数,控制哈希计算的"难度"。它是平衡安全性和性能的关键旋钮。
|
||
|
||
## 什么是成本因子?
|
||
|
||
bcrypt 不是做一次哈希就完事,而是通过 **多次迭代** 来增加暴力破解的计算成本。成本因子(cost)就是这个迭代次数的对数指数:
|
||
|
||
```
|
||
实际迭代次数 = 2^cost
|
||
```
|
||
|
||
所以在 Go 中:
|
||
|
||
```go
|
||
import "golang.org/x/crypto/bcrypt"
|
||
|
||
// bcrypt.DefaultCost = 10 → 2^10 = 1024 次迭代
|
||
hash, _ := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.DefaultCost)
|
||
```
|
||
|
||
## 不同成本因子的对比
|
||
|
||
| 成本值 | 迭代次数 | 单次哈希耗时 | 适用场景 |
|
||
|--------|---------|------------|---------|
|
||
| 4 | 16 | ~1ms | 本地开发、测试 |
|
||
| 8 | 256 | ~50ms | 低流量内部系统 |
|
||
| **10** | 1,024 | ~100-300ms | **生产环境默认** |
|
||
| 12 | 4,096 | ~500ms | 高安全需求 |
|
||
| 14 | 16,384 | ~2s | 极低频登录场景 |
|
||
|
||
> 💡 **想一想**:成本因子是不是越高越好?为什么?
|
||
>
|
||
> 答案:不是。成本越高,合法用户登录时服务器也要做同等次数的计算。过高的成本会导致登录延迟,影响用户体验,也给服务器带来不必要的负载。
|
||
|
||
## 如何选择合适的成本?
|
||
|
||
### 选择原则
|
||
|
||
```
|
||
目标:让单次哈希耗时在 100ms ~ 300ms 之间
|
||
```
|
||
|
||
这个区间意味着:
|
||
- **对合法用户**:几乎无感,登录流畅
|
||
- **对攻击者**:暴力破解速度被大幅压制(每秒只能尝试 3~10 次)
|
||
|
||
### 调整方式
|
||
|
||
```go
|
||
// 生产环境手动调高
|
||
hash, _ := bcrypt.GenerateFromPassword([]byte(pwd), 12)
|
||
|
||
// 也可以手动降低(仅开发环境)
|
||
hash, _ := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.MinCost) // MinCost = 4
|
||
```
|
||
|
||
## 成本因子的"升级陷阱"
|
||
|
||
一个容易被忽视的问题:当硬件变快后,原来合适的成本因子会变得不够安全。
|
||
|
||
```go
|
||
// 2024 年的机器比 2018 年快很多
|
||
// 当年 cost=10 是 200ms,现在可能只要 80ms
|
||
|
||
// 解决方案:用户登录时检查并升级哈希
|
||
func Login(username, password string) error {
|
||
user := db.GetUser(username)
|
||
bcrypt.CompareHashAndPassword(user.Hash, []byte(password))
|
||
|
||
// 检测当前哈希的成本是否偏低
|
||
currentCost, _ := bcrypt.Cost(user.Hash)
|
||
if currentCost < bcrypt.DefaultCost {
|
||
// 用更高的成本重新哈希并保存
|
||
newHash, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
|
||
db.UpdateHash(username, newHash)
|
||
}
|
||
}
|
||
```
|
||
|
||
> 💡 **想一想**:为什么要在登录时"顺便"升级哈希,而不是批量扫描全量用户?
|
||
>
|
||
> 答案:惰性升级(lazy upgrade)避免一次性计算压力过大,且只升级活跃用户的哈希,不活跃用户的旧哈希影响较小。
|
||
|
||
## 最佳实践
|
||
|
||
✅ **推荐**
|
||
- 生产环境默认 `bcrypt.DefaultCost`(10)
|
||
- 定期评估并逐步提升成本因子
|
||
- 登录时惰性升级低于最低成本的哈希
|
||
|
||
❌ **避免**
|
||
- 在生产环境使用 `< 10` 的成本
|
||
- 为每个用户设置不同的成本(增加复杂度且收益有限)
|
||
- 为了追求极致安全而盲目调高到 14+
|
||
|
||
## 关联笔记
|
||
|
||
- [[金山办公作业/Week06/登录安全流程]] - 登录安全全流程
|
||
- [[CS/DB/SQL安全]] - 密码安全存储
|