我使用golang.org/x/crypto/bcrypt
和GORM(http://gorm.io/docs/)来加密密码。问题在于,每次加密都不同,因此它永远无法与数据库中的加密匹配。
var result []string
password := []byte(data.Password)
encryptedPassword, err := bcrypt.GenerateFromPassword(password, bcrypt.DefaultCost) // different every time
db.Where(&User{Username: strings.ToLower(data.Username)}).First(&user).Pluck("password", &result)
encryptionErr := bcrypt.CompareHashAndPassword(encryptedPassword, []byte(result[0]))
if encryptionErr == nil { // passwords match! }
我已确认每次输入均相同,并且数据库提供的密码正确。
我在这里做什么错了?
答案 0 :(得分:4)
问题在于每种加密方式每次都不相同,因此它永远无法与数据库中的加密方式匹配。
这是正常的bcrypt行为。
bcrypt每次都会返回不同的哈希值,因为它会将不同的随机值合并到哈希值中。这就是所谓的“盐”。它可以防止人们使用“彩虹表”来攻击您的哈希密码,该彩虹表是一个预先生成的将密码哈希映射回其密码的表。盐表示,密码不是2个哈希值,而是1个哈希值。存储太多。
盐作为哈希密码的一部分存储。因此bcrypt.CompareHashAndPassword(encryptedPassword, plainPassword)
可以使用与plainPassword
相同的盐加密encryptedPassword
并进行比较。
请参见this answer for more information和达斯汀·波斯威尔的出色Storing User Passwords Securely: hashing, salting, and Bcrypt。
我在这里做什么错了?
您正在尝试将生成的哈希密码与存储的哈希密码进行比较。至少我当然希望它是存储在数据库中的哈希密码。
您想要的是将存储的哈希密码与用户输入的普通密码进行比较。
// Normally this comes from user input and is *never* stored
plainPassword := "supersekret"
// The encrypted password is stored in the database
db.Where(&User{Username: strings.ToLower(data.Username)}).First(&user).Pluck("password", &result)
encryptedPassword := []byte(result[0])
// Check if the stored encrypted password matches "supersekret"
encryptionErr := bcrypt.CompareHashAndPassword(encryptedPassword, plainPassword)
if encryptionErr == nil {
fmt.Println("Greetings Professor Falken")
} else {
fmt.Println(encryptionErr)
}
答案 1 :(得分:1)
根据设计,bcrypt hash algorithm每次调用时都会生成一个不同的加密字符串(它是salted)。如果您具有要检查的纯文本密码和数据库中的密文,则应该能够将这两件事传递给bcrypt.CompareHashAndPassword
。修改代码:
var result []string
db.Where(&User{Username: strings.ToLower(data.Username)})
.First(&user)
.Pluck("password", &result)
encryptionErr := bcrypt.CompareHashAndPassword([]byte(result[0]), []byte(data.Password))
您无需再次致电bcrypt.GenerateFromPassword
;正如您所注意到的,它将生成一个不同的加密密码,几乎不可能将两者进行相等性比较。