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源码,包括生成随机盐值、安全哈希值和修改迭代次数。文中通过一个示例,演示了如何使用进行加密、校验的过程。

参考

相关推荐
yuuki2332331 天前
【C语言】文件操作(附源码与图片)
c语言·后端
IT_陈寒1 天前
Python+AI实战:用LangChain构建智能问答系统的5个核心技巧
前端·人工智能·后端
无名之辈J1 天前
系统崩溃(OOM)
后端
码农刚子1 天前
ASP.NET Core Blazor简介和快速入门 二(组件基础)
javascript·后端
间彧1 天前
Java ConcurrentHashMap如何合理指定初始容量
后端
catchadmin1 天前
PHP8.5 的新 URI 扩展
开发语言·后端·php
少妇的美梦1 天前
Maven Profile 教程
后端·maven
白衣鸽子1 天前
RPO 与 RTO:分布式系统容灾的双子星
后端·架构
Jagger_1 天前
SOLID原则与设计模式关系详解
后端
间彧1 天前
Java: HashMap底层源码实现详解
后端