Golang中密码编码的实现与最佳实践
Golang中密码编码的实现与最佳实践 我正在接手一个多年前其他开发者用PHP编写的项目。我想编写一个能实现相同功能的Go应用程序,但对一些事情感到困惑。
以下是我试图用Go重新实现的PHP代码:
$salt = substr($correctHash, 0, 64); //从数据库当前哈希值的前部获取盐值
$validHash = substr($correctHash, 64, 64); //SHA256哈希值
$testHash = hash("sha256", $salt . $password); //创建新哈希值与原始值比较
注意:$password是用户发送的需要验证的文本短语,$correctHash是存储在数据库中用于对比的哈希值。
我的第一个问题是:看起来他最初将盐值与密码的哈希值一起存储。他从数据库存储的哈希值前部提取盐值,然后使用前64个字符作为盐值来处理用户刚刚传入的新调用。这是处理盐值的正确方式吗?因为这种方式似乎让盐值失去了意义。
其次,我很难弄清楚如何在Go中使用盐值执行sha256来获取哈希键。有人能给我指点一下如何实现这个的例子吗?
谢谢,
G
更多关于Golang中密码编码的实现与最佳实践的实战教程也可以访问 https://www.itying.com/category-94-b0.html
谢谢,是的,我已经看过了,但没明白如何向该函数传入盐值。
cybercrypt13:
如何进行sha256哈希计算
Go标准库提供了sha256包。
你好,
只是简单提一下,对于密码来说,使用比sha256更强的加密方式也是个好主意,比如bcrypt,它还能自动处理加盐部分。
这里有一个示例:
func main() {
fmt.Println("hello world")
}
感谢您的确认,确实如此。我昨天终于理解了盐值的作用点。😊
盐值就是前64个字节,而实际的哈希值位于末尾。因此我们在用户每次登录时将其剥离,以便将新生成的哈希值与密码字段末尾存储的最后65个字节进行比对。
我会尝试您提供的示例,看看能否成功实现。
再次感谢您提供的所有帮助。
你好 @cybercrypt13,
根据你获取盐值的示例,我猜测盐值是在密码哈希处理后添加在前缀位置的。如果是这种情况,你可以直接使用类似这样的方法:
func main() {
fmt.Println("hello world")
}
此外,确实,直接将盐值添加到哈希值前面并不是最佳做法。我认为更常见的做法是:在添加盐值后进行哈希计算,然后将原始盐值存储在数据库的某个位置,在进行比较时再获取(至少这是我的理解)。
感谢,我已经在考虑这个方法了,但在采用之前,我需要在登录过程中转换当前的密码哈希值。这很容易实现,但在替换之前,我需要能够根据旧的盐值和哈希值验证用户提供的密码是否正确。
// 验证密码的示例代码
func verifyPassword(oldHash []byte, salt []byte, inputPassword string) bool {
// 这里实现密码验证逻辑
// 使用旧盐值对输入密码进行哈希
// 比较生成的哈希与存储的旧哈希
return compareHashes(oldHash, newHash)
}
1个点赞
你好 @cybercrypt13,
抱歉,我误读了你在示例中原来的第3行!
无论如何,在这种情况下,你不是从数据库中的哈希密码单独检索盐吗?我认为你不能简单地从加盐哈希中截取字符并“重复使用”它来为新哈希加盐以比较值。
$salt = substr($correctHash, 0, 64);
这是我提到的部分。correctHash 包含什么?它是否存储了完整的盐,其中0-64位是盐,65位之后是实际的哈希?如果是这样,那对我来说就更合理了 🙂
例如,在保存密码时进行初始哈希后,你是否以某种方式存储它,像这样。
$salt = bin2hex(random_bytes(32)); // 新盐
$newpass = hash("sha256", $salt . $password); // 创建哈希密码
/* 以某种方式将密码存储在用户中,密码设置为 salt + hash */
如果是这种情况,这里有一个不同的示例来处理这个问题 🙂:
现在意识到盐值实际上是经过哈希处理的,这是正确的做法,绝对不是无意义的。
这意味着黑客无法使用彩虹表来匹配所有未加盐的密码,而是必须为每个密码单独获取盐值(因为每个密码都有唯一的盐),基于该盐值为每个密码生成新的彩虹表,然后从该特定的新彩虹表进行比较,这将极其耗时且不可行。因此,即使他们能够破解一个密码,破解数据库中的所有密码也将花费大量时间。
从我在这里找到的一个回答中,解释得可能比我更好(为什么加盐不会使哈希失效?- 密码学 Stack Exchange):
盐值以明文形式存储在哈希旁边,并且在检查密码时使用相同的盐值。
其目的是减缓获取数据库副本的攻击者的速度:
- 他们必须单独攻击每个密码(他们需要为每个哈希尝试每个输入,而不是尝试每个输入并将输出与所有哈希一次性比较)。
- 他们无法利用巨大的预计算查找表(“彩虹表”),因为加盐本质上相当于为每个用户提供自己的哈希函数。
无需将盐值保密或比相应的哈希更安全地存储。
在Go中实现与您描述的PHP代码相同的密码验证逻辑是可行的。我来解释一下这个设计的合理性并提供Go的实现示例。
首先,关于盐值存储的问题:将盐值与哈希值一起存储在同一个字段中是一种常见的做法。虽然盐值本身不是秘密,但它的主要作用是防止预计算攻击(如彩虹表)。即使攻击者知道盐值,他们仍然需要为每个盐值单独计算哈希表,这大大增加了攻击成本。所以这种方式并没有让盐值"失去意义"。
以下是Go的实现示例:
package main
import (
"crypto/sha256"
"encoding/hex"
"fmt"
)
// 验证密码函数
func verifyPassword(password, correctHash string) bool {
if len(correctHash) < 128 {
return false
}
// 从存储的哈希值中提取盐值和原始哈希
salt := correctHash[:64]
originalHash := correctHash[64:128]
// 使用盐值和密码计算新的哈希
hasher := sha256.New()
hasher.Write([]byte(salt + password))
testHash := hex.EncodeToString(hasher.Sum(nil))
return testHash == originalHash
}
// 创建密码哈希(用于新用户注册或密码更改)
func createPasswordHash(password string) string {
// 生成随机盐值(实际应用中应该使用crypto/rand)
salt := generateSalt()
// 计算盐值+密码的哈希
hasher := sha256.New()
hasher.Write([]byte(salt + password))
passwordHash := hex.EncodeToString(hasher.Sum(nil))
// 返回盐值+哈希的组合
return salt + passwordHash
}
// 生成64字符的随机盐值
func generateSalt() string {
// 在实际应用中,应该使用crypto/rand生成安全的随机字节
// 这里简化为示例
randomBytes := make([]byte, 32)
// 在实际代码中应该使用:rand.Read(randomBytes)
return hex.EncodeToString(randomBytes)
}
// 使用示例
func main() {
// 创建新密码哈希(注册时使用)
password := "user_password"
hashedPassword := createPasswordHash(password)
fmt.Printf("存储的哈希值: %s\n", hashedPassword)
// 验证密码(登录时使用)
isValid := verifyPassword("user_password", hashedPassword)
fmt.Printf("密码验证结果: %t\n", isValid) // 应该输出 true
isInvalid := verifyPassword("wrong_password", hashedPassword)
fmt.Printf("错误密码验证结果: %t\n", isInvalid) // 应该输出 false
}
对于现代密码哈希,我建议考虑使用更安全的方案:
import "golang.org/x/crypto/bcrypt"
// 使用bcrypt的示例
func modernPasswordHash(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
if err != nil {
return "", err
}
return string(hash), nil
}
func modernVerifyPassword(password, hash string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
return err == nil
}
bcrypt会自动处理盐值生成和存储,并且提供了更好的安全性,因为它使用了自适应成本因子来抵御暴力破解攻击。


