golang使用bcrypt实现密码加密和验证

介绍

把用户的密码存入数据库的时候,我们当然不能使用明文存储,要对密码进行一次加密,然后再存储到数据库中。

我们一般采用哈希算法实现对密码的加密,因为哈希算法得到的加密数据是不可逆的

哈希算法(Hash Algorithm)是一种将任意长度的数据映射为固定长度数据的算法。哈希算法广泛应用于数据完整性校验、密码存储、数字签名等领域。以下是一些常见的哈希算法:

简单的哈希算法

1. MD5 (Message Digest Algorithm 5)

  • 输出长度: 128位(16字节)
  • 特点: 速度快,但安全性较低,容易受到碰撞攻击。
  • 应用: 文件校验、旧版密码存储。

2. SHA-1 (Secure Hash Algorithm 1)

  • 输出长度: 160位(20字节)
  • 特点: 安全性较MD5高,但仍存在碰撞风险。
  • 应用: 数字签名、SSL证书。

3. SHA-2 (Secure Hash Algorithm 2)

  • 子算法: SHA-224, SHA-256, SHA-384, SHA-512
  • 输出长度: 224位、256位、384位、512位
  • 特点: 安全性高,广泛使用。
  • 应用: 数字签名、SSL/TLS、区块链。

4. SHA-3 (Secure Hash Algorithm 3)

  • 子算法: SHA3-224, SHA3-256, SHA3-384, SHA3-512
  • 输出长度: 224位、256位、384位、512位
  • 特点: 基于Keccak算法,与SHA-2结构不同,安全性高。
  • 应用: 数字签名、密码存储。

5. RIPEMD-160 (RACE Integrity Primitives Evaluation Message Digest 160)

  • 输出长度: 160位(20字节)
  • 特点: 安全性较高,常用于比特币地址生成。
  • 应用: 数字签名、区块链。

6. BLAKE2

  • 输出长度: 可变,最长512位
  • 特点: 速度快,安全性高。
  • 应用: 文件校验、密码存储。

7. Whirlpool

  • 输出长度: 512位
  • 特点: 安全性高,基于AES加密算法。
  • 应用: 数字签名、密码存储。

8. CRC32 (Cyclic Redundancy Check 32)

  • 输出长度: 32位
  • 特点: 主要用于数据校验,速度快,但安全性较低。
  • 应用: 数据传输校验、文件校验。

9. MurmurHash

  • 输出长度: 32位或128位
  • 特点: 非加密哈希函数,速度快,适用于非加密场景。
  • 应用: 数据库索引、分布式缓存。

10. xxHash

  • 输出长度: 32位或64位
  • 特点: 速度极快,适用于高性能场景。
  • 应用: 数据校验、分布式系统。

总结

  • 安全性: SHA-2、SHA-3、BLAKE2、RIPEMD-160 等算法安全性较高。
  • 速度: MD5、SHA-1、MurmurHash、xxHash 等算法速度较快。
  • 应用场景: 根据具体需求选择合适的哈希算法,如密码存储建议使用SHA-256或更高强度的算法,数据校验可使用CRC32或MD5。

选择哈希算法时,应考虑安全性、速度和应用场景,确保数据的安全性和效率。

下面我们介绍一下彩虹表

彩虹表

彩虹表是一种用于破解哈希密码的预计算技术,通过预先计算大量常见密码的哈希值,并将这些哈希值与对应的原始密码存储在一张表中,从而在破解的时候能快速查找与哈希值匹配的密码

彩虹表的工作原理

  1. 预计算阶段
    • 生成大量常见密码的哈希值。
    • 将每个密码的哈希值与原始密码存储在表中。
  2. 查找阶段
    • 当攻击者获得一个哈希值时,可以通过查找彩虹表,快速找到与之匹配的原始密码。

彩虹表的优势

  • 速度快:相比于暴力破解(Brute Force)和字典攻击(Dictionary Attack),彩虹表能够在短时间内找到匹配的密码。
  • 存储效率:彩虹表通过使用一种称为“链”(Chain)的技术,减少了存储空间的需求。每个链由一系列哈希值和对应的还原值组成,最终只存储链的起点和终点。

彩虹表的生成

  1. 选择初始密码:从常见密码列表中选择一个初始密码。

  2. 生成链

    • 对初始密码进行哈希运算,得到哈希值。
    • 对哈希值应用一个还原函数(Reduction Function),得到一个新的密码。
    • 重复上述步骤,生成一条链。
  3. 存储链:只存储链的起点和终点,中间的哈希值和还原值不存储。

彩虹表的局限性

  • 存储需求:虽然彩虹表通过链技术减少了存储空间,但仍然需要大量的存储资源。
  • 碰撞问题:不同的密码可能会生成相同的哈希值,导致链中的某些部分无法正确还原。
  • 加盐技术:使用加盐(Salt)技术可以有效防止彩虹表攻击。加盐是指在密码哈希过程中加入一个随机值,使得相同的密码在不同的用户中生成不同的哈希值。

如何防御彩虹表攻击

  1. 加盐:在哈希过程中加入一个随机值(盐),使得相同的密码在不同的用户中生成不同的哈希值。
  2. 使用强哈希算法:选择安全性高的哈希算法,如SHA-256或更高强度的算法。
  3. 增加迭代次数:通过多次迭代哈希运算,增加破解的难度和时间成本。

什么是哈希(Hash)? - 知乎 (zhihu.com)

目前,MD5和Bcrypt比较流行。

BCrypt加密: 一种加盐的单向Hash,不可逆的加密算法,同一种明文(plaintext),每次加密后的密文都不一样,而且不可反向破解生成明文,破解难度很大。

MD5加密: 是不加盐的单向Hash,不可逆的加密算法,同一个密码经过hash的时候生成的是同一个hash值,在大多数的情况下,有些经过md5加密的方法将会被破解。

作者:打代码去
链接:https://juejin.cn/post/7107997489029971981
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

使用

下面我们来使用go中的bcrypt算法对写两个函数,实现对密码的加密

首线我们需要装一下这个算法的包

 go get golang.org/x/crypto/bcrypt

image-20240915095133277

bcrtpy库有两个函数,分别是ComparePwd(pwd1 string,pwd2 string),GernerateFromPassword(pwd []byte,cost int) ([]byte,error)

pwd1 是数据库的已经加密的密码, pwd2 是用户实际的密码。

我们来写一下这个工具函数

package main

import (
	"fmt"
	"log"

	"golang.org/x/crypto/bcrypt"
)

func HashPwd(pwd string) string {
	hash, err := bcrypt.GenerateFromPassword([]byte(pwd), bcrypt.MinCost)
	if err != nil {
		log.Println(err)
	}
	return string(hash)
}
func CheckPwd(hashPwd string, userPwd string) bool {
	byteHash := []byte(hashPwd)
	err := bcrypt.CompareHashAndPassword(byteHash, []byte(userPwd))
	if err != nil {
		log.Println(err)
		return false
	}
	return true
}
func main() {
	userpwd := "meowrian"
	hashpwd := HashPwd(userpwd)
	if CheckPwd(hashpwd, userpwd) {
		fmt.Println("equal")
	}
}

image-20240915095950497

上面是一个简单示例,我们可以把这两个函数用在实践中,比如在使用gorm进行存储用户密码的时候,将密码加密后存储在数据库中,当用户下次登录的时候,就需要我们进行密码比对了,这个时候我们从前端取到用户的密码,然后调用CheckPwd()对密码进行校验,如果可以的到true,那就说明用户的密码正确,反之不正确