一、前言
当涉及存储和处理敏感信息,特别是密码时,安全性是至关重要的。
在过去某段时间,一些网站就遭遇了用户密码数据泄露事件,类似事件给用户的生活造成巨大影响。特别是部分人群习惯性地使用相同的密码,则有一家"暴库泄露",全部遭殃之说。
因此,在应用程序中存储用户密码时,通常不建议明文存储,否则一旦数据库泄漏,用户的敏感信息就会暴露。相反,我们应该存储密码的哈希值,这样即使哈希值泄漏,攻击者也难以还原出原始密码。
在本次大作业中,我使用了 Bcrypt
,对用户的登录密码进行加密。以下是 Bcrypt
的实践记录。
二、内容
2.1 什么是 Bcrypt
Bcrypt
(Blowfish Crypt)是一种哈希函数,专门用于密码哈希和密码存储,以提供更高的安全性。它与传统的哈希算法(如MD5
、SHA-256
和SHA-1
等)相比,具有更强的安全性,适用于密码加密和存储。
Bcrypt
的安全性主要体现在以下几个方面:
- 慢速哈希函数: Bcrypt是一种"慢速"哈希函数,这意味着它需要更长的时间来计算哈希值。这是故意的,以增加破解者猜测密码的成本和难度。由于计算密集型的特性,攻击者需要投入大量的时间和计算资源来尝试破解哈希值,从而提高了系统的安全性。
- 加盐(Salting): Bcrypt自动地使用"盐"来加强密码哈希的安全性。盐是一个随机的字符串,它会与密码一起哈希。由于每个用户的盐都是唯一的,即使两个用户使用相同的密码,最终的哈希结果也会不同。这有效地防止彩虹表攻击,因为攻击者不能简单地将常见的密码对应的哈希结果预先计算出来。
- 可调性: Bcrypt允许您通过增加"工作因子"来调整哈希函数的复杂性。工作因子是一个控制迭代次数的参数,它影响每次哈希运算所需的时间。通过增加工作因子,您可以使哈希过程更慢,从而增加暴力破解的难度。
- 哈希存储格式: Bcrypt生成的哈希值的长度为60个字符,这包括哈希版本、工作因子、盐值和实际的哈希结果。这种格式使得验证密码时可以轻松地从哈希中提取所需的信息。
因此,使用 Bcrypt
来确保用户数据的安全性是一种不错的方案。
2.2 代码演示
在Golang中,使用 golang.org/x/crypto/bcrypt
包可以轻松地实现密码的哈希加密和验证。
(1)导包
首先,我们需要确保在代码中导入了golang.org/x/crypto/bcrypt
包。在代码的开头添加以下导入语句:
go
package utils
import (
"golang.org/x/crypto/bcrypt"
)
(2)加密
在加密的过程中,生成密码的哈希值是确保密码安全的第一步。
go
// EncryptPassword 生成密码哈希,使用默认的哈希迭代成本(cost)
func EncryptPassword(password string) ([]byte, error) {
// 生成随机的盐并将密码哈希化
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
return hashedPassword, err
}
在上面的代码中:
EncryptPassword
函数接受一个用户提供的密码作为参数,并返回一个哈希化后的密码。bcrypt.GenerateFromPassword
函数会生成一个随机的盐并将密码与盐一起哈希化。bcrypt.DefaultCost
是一个默认的哈希迭代成本,它决定了哈希函数的迭代次数,从而影响安全性和计算时间。
(3)验证
一旦密码的哈希值生成,我们需要有一种方法来验证用户提供的密码是否与存储的哈希值匹配。这就是密码验证的过程。我们可以使用bcrypt.CompareHashAndPassword
函数来实现这一点:
go
// CheckPasswordValidity 将加密后的密码和用户提供的密码进行比较
func CheckPasswordValidity(encryptedPassword, userPassword string) bool {
// 将加密后的密码与用户提供的密码进行比较
err := bcrypt.CompareHashAndPassword([]byte(encryptedPassword), []byte(userPassword))
return err == nil
}
在上面的代码中:
CheckPasswordValidity
函数接受加密后的密码和用户提供的密码作为参数,并返回一个布尔值,表示密码是否匹配。bcrypt.CompareHashAndPassword
函数会将加密后的密码 与用户提供的密码 进行比较,如果匹配则返回true
,否则返回false
。
(4)使用
这里简单演示一下如何使用上述编写的 utils
包中的工具函数。
记得导包:
goimport ( "golang.org/x/crypto/bcrypt" "github.com/gin-gonic/gin" )
这里演示一下
首先设置路由来处理注册和登录请求。
go
func main() {
r := gin.Default()
r.POST("/register", Register)
r.POST("/login", Login)
r.Run(":8080")
}
接着模拟实现用户注册和登录功能。
go
func Register(c *gin.Context) {
// 获取用户提交的用户名和密码
username := c.Query("username")
password := c.Query("password")
// 生成密码哈希
hashedPassword, err := utils.EncryptPassword(password)
if err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to generate password hash"})
return
}
// 将用户名和密码哈希存储到数据库中
// ...
c.JSON(http.StatusOK, gin.H{"message": "User registered successfully"})
}
func Login(c *gin.Context) {
// 获取用户提交的用户名和密码
username := c.Query("username")
password := c.Query("password")
// 从数据库中获取密码哈希
hashedPassword := // Retrieve hashed password from the database based on username
// 验证用户密码
if utils.CheckPasswordValidity(hashedPassword, password) != true {
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invalid password"})
return user
}
// 登录成功逻辑
// ...
c.JSON(http.StatusOK, gin.H{"message": "Logged in successfully"})
}
可以看到:
- 在注册时,你需要将用户提供的密码进行加密并存储到数据库中,
- 在登录时,你需要将用户提供的密码与数据库中的加密密码进行验证。
三、总结
在本文中,我们学习了如何使用golang.org/x/crypto/bcrypt
包在Golang中进行密码哈希生成和验证。通过使用加盐哈希算法,我们可以确保用户密码的安全性,即使在敏感数据泄露的情况下也能保护用户的密码。另外,我们还可以通过使用自定义的哈希迭代成本来平衡安全性和性能之间的关系,进一步提高数据安全性。在开发应用程序时,务必采取必要的安全措施来保护用户的敏感信息。