加密算法总结
目录
问题:bcrypt,如果用一个字符串加密两次,得到的加密字符串相同吗?
JavaGuide:常见加密算法总结
PS:本文对上面的学习资料进行补充,请务必要学习上面的资料。
关于密码加密相关的关键词有:
哈希加密(包含加密哈希算法、非加密哈希算法,慢哈希加密是哈希加密的一种特殊设计)、对称加密,非对称加密、加盐、成本因子、碰撞、撞库、彩虹表
哈希加密,对称加密,非对称加密三者区别:
哈希用于密码存储和完整性校验,不涉及密钥,哈希算法是单向的,不可逆,只能加密,不能解密,因此数据库中存储的是单向转换后的密码,在进行用户身份验证时需要将用户输入的密码进行单向转换,然后与数据库的密码进行比较;
对称加密(如AES)用同一密钥加密/解密,适合大数据量处理;
非对称加密(如RSA)用公钥/私钥对,解决密钥分发问题。
实际应用中,对称加密和非对称加密常结合使用。例如HTTPS先用RSA交换AES密钥,再用AES加密通信数据。
三者的区别是:哈希用于存储,对称/非对称加密既可用于传输也可用于存储,但非对称加密因性能问题通常不直接加密大数据。
MD5
- 彩虹表攻击:可以撞库
- 碰撞攻击:MD5已被证明可人为制造碰撞(两个不同输入产生相同哈希)
- 速度过快:现代硬件可每秒计算数十亿次MD5哈希
- 无盐值:直接MD5容易受到彩虹表攻击
- 密码加密
- 文件的完整性校验:在传输一个文件的时候,在发送端计算一次文件的MD5,并把这个结果发送给接收端,接收端在文件传输后再根据传输过来的文件计算一次MD5,然后比较这两个MD5是否相同,如果相同,那么说明文件没有缺失或者被修改。
- 数字签名:比如发布了一个程序,为了防止程序被植入木马,在发布程序的时候,同时发布这个程序的MD5,别人在下载程序的时候,计算一下MD5然后比较一下。
- 云盘秒传:有时候上传一个很大的文件几乎是秒传,并不是真的把文件上传了,而是计算一下文件的MD5,并在自己的数据库中搜索一下这个MD5是否存在,如果存在就不用上传了,直接使用已经存在的文件就行了,从而实现了文件秒传。
bcrypt加密
- 不可逆加密:bcrypt 是一种哈希算法,不是对称或非对称加密,生成的哈希值是不可逆的。这意味着你无法通过哈希值还原出原始密码。
- 抗暴力破解:bcrypt 设计时考虑了抗暴力破解攻击,它通过引工作因子(Cost Factor) 控制算法运行的时间复杂度,增加了暴力破解的难度。bcrypt.DefaultCost 是默认的工作因子,通常是 10。
- 哈希加盐:bcrypt 内部自动对每个密码进行 加盐(salt),也就是在密码哈希前,添加一个随机生成的值。这避免了彩虹表攻击,因为同一个密码每次加密的结果都会不同。
package main
import (
"fmt"
"golang.org/x/crypto/bcrypt"
)
// bcrypt.DefaultCost 是一个默认的计算成本参数,可以影响加密和解密的速度,通常值为 10。你可以根据需要调整这个值,越高的值会增加安全性但也会降低速度。
// 加密后的密码是一个字符串,可以直接存储在数据库中。
// 验证密码时,只需将用户输入的密码与数据库中存储的哈希密码进行比较。
// HashPassword 加密密码。使用 bcrypt.GenerateFromPassword() 方法来加密密码。
func HashPassword(password string) (string, error) {
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hashedPassword), nil
}
// CheckPasswordHash 验证密码。使用 bcrypt.CompareHashAndPassword() 方法来验证输入的密码是否正确。
func CheckPasswordHash(password, hashedPassword string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
return err == nil
}
func main() {
// 用户输入的密码
inputPassword := "Ab123456"
encryptedPassword, _ := HashPassword(inputPassword)
fmt.Println("加密密码为", encryptedPassword)
p := "Ab123456" // 用户再次输入的密码
// 比较用户输入的密码和数据库中的哈希密码
if CheckPasswordHash(p, encryptedPassword) {
fmt.Println("密码正确") // 密码正确
} else {
fmt.Println("密码错误")
}
if CheckPasswordHash(p, inputPassword) {
fmt.Println("密码正确")
} else {
fmt.Println("密码错误") // 密码错误
}
}
问题:bcrypt,如果用一个字符串加密两次,得到的加密字符串相同吗?
为什么“慢哈希”能阻止攻击者?
bcrypt.hash(password, cost=12) // 迭代2^12=4096次
- 合法用户:每次登录只需计算 1次 慢哈希(比如300ms)
- 攻击者:如果尝试暴力破解(每秒100万次),现在只能尝试 ~3次/秒(因为每次哈希要300ms),所以破解一个8位密码的时间可能要几百年。
- 在线攻击(如撞库):直接向服务器发送大量密码尝试登录。这样慢哈希就可以直接拖慢攻击速度,服务器可以轻松检测并封禁异常请求。
- 离线攻击(如数据库泄露后破解哈希):攻击者拿到哈希值后,在自己的机器上暴力破解。慢哈希会强制攻击者花费更多计算资源:原本MD5每秒可计算10亿次,现在bcrypt每秒只能计算几千次。生成彩虹表的成本从 1TB硬盘+1天 变成 需要整个Google的数据中心+100年。
- 输入:密码 + 随机盐(Salt)。
- 迭代加密:使用Blowfish算法,反复加密盐和密码的组合(如迭代4096次)。每次迭代都依赖前一次结果,无法并行优化。
- 输出:最终的哈希值(包含盐和成本因子)。
加盐
盐(Salt)在密码学中,是指通过在密码任意固定位置插入字符串,让哈希后的结果和使用原始密码的哈希结果不相符,这种过程称之为“加盐”。在密码加盐处理后,同一个原始密码每次生成的字符串通常是不一样的。
盐值的作用和原理:
在密码存储过程中,加盐是一种增强密码安全性的常见技术。“盐” 是一个随机生成的字符串,它会与用户的原始密码相结合,然后再进行哈希运算。这样做的主要目的是防止彩虹表攻击,因为即使多个用户使用相同的密码,由于盐值不同,经过哈希处理后的结果也会不同。
特殊情况:在某些特定的场景下,也可以使用固定的盐值。例如,为了某种特定的业务需求或者在测试环境中,可能会使用一个固定的字符串作为盐值。在这种情况下,同一个密码每次生成的加盐哈希字符串是相同的,但这种做法会降低密码的安全性,不建议在生产环境中使用。
成本因子
- 哈希计算的迭代次数(直接影响计算时间)。
- 计算资源的消耗(CPU/内存占用)。
- 最终的安全强度(破解难度)。
- cost=10 → 2^10 = 1,024 次迭代
- cost=12 → 2^12 = 4,096 次迭代(推荐默认值)
- cost=15 → 2^15 = 32,768 次迭代(更高安全性)
| 成本因子(cost) | 迭代次数 | 单次哈希时间(示例) | 破解10亿密码所需时间 |
| 10 | 1,024 | ~5ms | ~2个月(GPU集群) |
| 12(推荐) | 4,096 | ~300ms | ~200年 |
| 15 | 32,768 | ~2.5s | ~16,000年 |
问题1: 成本因子是否可以无限增大?
答:不可以。需要平衡安全性和用户体验:成本过高会导致登录延迟(如cost=20需30秒计算),用户无法接受。通常选择使单次哈希在100ms~1s之间的cost值。
问题2: 如果未来硬件变快,是否需要调整cost?
答:是的!建议每2~3年增加1次cost(如从12→13),以抵消硬件进步带来的破解速度提升。
问题3: bcrypt的cost和PBKDF2的iterations有何区别?
答:
bcrypt.cost=12 → 固定迭代2^12=4096次。
PBKDF2.iterations=100000 → 直接指定迭代次数。两者本质相同,但bcrypt的cost更直观(指数增长)。
彩虹表
- 预计算阶段:攻击者使用密码哈希函数和彩虹表生成算法,预先计算大量的哈希值和对应的明文密码,并将结果存储在一个庞大的彩虹表中。这个表可以包含数亿条数据项。
- 破解阶段:当攻击者获得了存储在目标系统中的密码哈希值后,他们可以在彩虹表中查找匹配项。如果哈希值在彩虹表中找到了匹配,攻击者就可以通过查找对应的明文密码来获取用户的原始密码。
- 彩虹表攻击的成功率受到彩虹表的规模和生成算法的影响。如果目标密码很复杂,彩虹表需要更大的规模才能成功破解。
- 一些安全措施可以减轻彩虹表攻击的影响,如“盐”(Salt)。盐是一个随机值,附加在用户密码上然后一同进行哈希。这使得即使两个用户使用相同的密码,由于不同的盐,他们的哈希值也是不同的。
- 使用更强大、更缓慢的哈希函数(如bcrypt、scrypt)可以大幅降低彩虹表攻击的成功率。
更多推荐
所有评论(0)