Go Libs :bcrypt (安全密码加密)

前言

在系统开发中,为了保护用户的密码安全,不被恶意窃取,我们需要对密码进行加密和验证。本文将介绍Go语言如何使用bcrypt库来实现安全的密码加密。

首先,为什么要加密?试想,如果服务器的数据库被盗,那攻击者就可以拿着明文的密码进行登入篡改、信息泄露等。

一些基础

  • bcrypt: 是专门为密码存储而设计的算法,基于Blowfish加密算法变形而来,由Niels Provos和David Mazières发表于1999年的USENIX。
  • SALT值(盐值): 属于随机值.用户注册时,系统用来和用户密码进行组合而生成的随机数值,称作salt值,通称为加盐值。
  • 彩虹表: 预先计算出所有可能的hash值,比如6位密码的所有hash值(大小写加数字,只有62^6个)

加密过程与原理

当用户首次提供密码时(通常是注册时),由系统自动添加随机生成的salt值,然后再散列。而当用户登录时,系统为用户提供的代码撒上同样的加盐值,然后散列,再比较散列值,以确定密码是否正确。

为用户密码添加Salt值,使得加密的得到的密文更加冷僻,不宜查询。即使黑客有密文查询到的值,也是加了salt值的密码,而非用户设置的密码。salt值是随机生成的一组字符串,可以包括随机的大小写字母、数字、字符,位数可以根据要求而不一样。

bcrypt

bcrypt 包实现了 Provos 和 Mazières 的 bcrypt 自适应哈希算法。

加密过程与源码

  1. 生成盐值:在对密码进行哈希之前,需要生成一个随机的盐值。 源码参考:
go 复制代码
unencodedSalt := make([]byte, maxSaltSize)
_, err = io.ReadFull(rand.Reader, unencodedSalt)
if err != nil {
    return nil, err
} 
  1. 使用生成的盐值和密码进行哈希运算。源码参考:
go 复制代码
p.salt = base64Encode(unencodedSalt)
hash, err := bcrypt(password, p.cost, p.salt)
if err != nil {
    return nil, err
}
  1. 可选修改迭代次数来增加哈希函数的计算复杂度, 增加迭代次数会导致哈希函数的计算时间增加,从而增加攻击者破解密码的难度。源码参考:
go 复制代码
func expensiveBlowfishSetup(key []byte, cost uint32, salt []byte) (*blowfish.Cipher, error) {
 csalt, err := base64Decode(salt)
 if err != nil {
    return nil, err
 }

 // Bug compatibility with C bcrypt implementations. They use the trailing
 // NULL in the key string during expansion.
 // We copy the key to prevent changing the underlying array.
 ckey := append(key[:len(key):len(key)], 0)

 c, err := blowfish.NewSaltedCipher(ckey, csalt)
 if err != nil {
    return nil, err
 }

 var i, rounds uint64
 rounds = 1 << cost
 for i = 0; i < rounds; i++ {
    blowfish.ExpandKey(ckey, c)
    blowfish.ExpandKey(csalt, c)
 }

 return c, nil
}

使用Bcrypt进行密码加密的一个例子:

first 安装

arduino 复制代码
go get golang.org/x/crypto/bcrypt

Example:

go 复制代码
package main

import (
    "fmt"
    "golang.org/x/crypto/bcrypt"
)

func main() {
    var (
       password = "123456"
    )

    hashedPassword, err := HashPassword(password)
    if err != nil {
       fmt.Println("Error hashing password:", err)
       return
    }

    fmt.Println("未加密:", password)
    fmt.Println("加密后:", hashedPassword)

    if err := CheckPasswordHash("202020", hashedPassword); err != nil {
       fmt.Println("密码不匹配:", err)
    } else {
       fmt.Println("密码匹配")
    }

    if err := CheckPasswordHash(password, hashedPassword); err != nil {
       fmt.Println("密码不匹配:", err)
    } else {
       fmt.Println("密码匹配!")
    }
}

func CheckPasswordHash(password, hash string) error {
    return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
}

func HashPassword(password string) (string, error) {
    bytes, err := bcrypt.GenerateFromPassword([]byte(password), 10)
    return string(bytes), err
} 

执行输出:

js 复制代码
未加密: 123456
加密后: $2a$10$EvmU.zY9qGx7QntUFG9Qf.G6SRytkf8GMtFMsn1IYjnIMbZ6gUd4O
密码不匹配: crypto/bcrypt: hashedPassword is not the hash of the given password
密码匹配!

注意:

  • bcrypt接受最长密码的长度 72 字节,在使用时注意密码长度
  • 迭代次数 Cost 如果给定的成本小于 MinCost(4),则成本将改为 DefaultCos(10)

小结

为了保护用户的密码不被恶意攻击者窃取,如服务器数据库被盗、 碰撞、彩虹表等,可以通过使用Bcrypt算法加密。同时介绍了加密的过程和Go Bcrypt源码,包括生成随机盐值、安全哈希值和修改迭代次数。文中通过一个示例,演示了如何使用进行加密、校验的过程。

参考

相关推荐
一只小猪妖11 分钟前
分布式唯一 性ID生成方法
后端
小蒜学长17 分钟前
微信小程序-高校自习室管理系统(代码+数据库+LW)
java·spring boot·后端·微信小程序·自习室管理
M1A121 分钟前
告别代码风格之争:在 VS Code 中用 Black 一键格式化 Python
后端
IT_陈寒32 分钟前
Python多进程共享变量这个坑,差点让我加班到凌晨
前端·人工智能·后端
bug菌1 小时前
一个周末就想把短链服务推到 10 万 QPS,真能靠加 Redis 做到吗?
java·redis·go
撸串-研究生1 小时前
springboot福利院管理系统73979
java·spring boot·后端·福利院管理系统
xixiaoyunya1 小时前
别再写一堆 if-else 了:用策略模式重构你的业务代码
后端
tachibana22 小时前
Go 语言里的 Context 是什么?
开发语言·后端·golang·go·语言
Linux实战笔记2 小时前
sync.Mutex还是Channel?Go并发场景选型对比与goroutine泄漏实战
go·channel·sync.mutex·goroutine泄漏·race detector
鶴哥只手遮天2 小时前
Spring Framework 7.0.9 深度剖析-05-配置元数据解析:XML、注解与 JavaConfig
java·后端·开源