如何用Go语言验证/etc/shadow文件中的SHA512哈希密码
验证/etc/shadow中的SHA512密码哈希
问题描述
我的/etc/shadow文件中存储的密码哈希为$6$IcnB6XpT8xjWC$AI9Rq5hqpEP.Juts/TUbHk/OI7sO/S1AA.ihgBjHN12QmT5p44X5or86PsO9/oPBO4cmo0At4XuMC0yCApo87/,这是通过passwd或chpasswd生成的SHA512加密密码。我尝试用Go语言提取盐值后重新哈希用户输入的密码,但无法生成一致的哈希值,代码如下:
// /etc/shadow中存储的"test"的哈希值 myPwd := "$6$IcnB6XpT8xjWC$AI9Rq5hqpEP.Juts/TUbHk/OI7sO/S1AA.ihgBjHN12QmT5p44X5or86PsO9/oPBO4cmo0At4XuMC0yCApo87/" // 提取盐值 salt := strings.Split(myPwd, "$")[2] encoding := base64.NewEncoding("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz").WithPadding(base64.NoPadding) decodedSalt, err := encoding.DecodeString(salt) // 用户输入的密码 myNewPwd := "test" newHashedPwd, err := hashPwd512(string(myNewPwd), string(decodedSalt)) // 对比哈希值 if newHashedPwd == myPwd { // 密码验证通过 } // 期望生成和/etc/shadow中一致的哈希部分 // AI9Rq5hqpEP.Juts/TUbHk/OI7sO/S1AA.ihgBjHN12QmT5p44X5or86PsO9/oPBO4cmo0At4XuMC0yCApo87/ func hashPwd512(pwd string, salt string) (string, error) { hash := sha512.New() hash.Write([]byte(salt)) hash.Write([]byte(pwd)) encoding := base64.NewEncoding("./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz").WithPadding(base64.NoPadding) hashedPwd := encoding.EncodeToString(hash.Sum(nil)) return hashedPwd, nil }
错误原因
手动实现的哈希逻辑完全不符合Unix系统中SHA512密码哈希(SHA512-crypt)的标准算法。SHA512-crypt不是简单将盐和密码拼接后做一次SHA512哈希,而是包含多次迭代、特定的密钥混合步骤、固定的格式编码规则,必须遵循该算法才能生成一致的哈希值。
正确实现
使用Go官方的golang.org/x/crypto/crypt包中的SHA512Crypt工具来验证,该包已经实现了标准的SHA512-crypt算法:
package main import ( "fmt" "strings" "golang.org/x/crypto/crypt/sha512_crypt" ) func main() { shadowHash := "$6$IcnB6XpT8xjWC$AI9Rq5hqpEP.Juts/TUbHk/OI7sO/S1AA.ihgBjHN12QmT5p44X5or86PsO9/oPBO4cmo0At4XuMC0yCApo87/" inputPwd := "test" // 提取哈希中的盐部分($6$后面到下一个$之前的内容) parts := strings.Split(shadowHash, "$") if len(parts) != 4 || parts[1] != "6" { fmt.Println("无效的SHA512哈希格式") return } salt := "$6$" + parts[2] + "$" // 使用sha512_crypt验证密码 crypt := sha512_crypt.New() newHash, err := crypt.Generate([]byte(inputPwd), []byte(salt)) if err != nil { fmt.Printf("生成哈希失败: %v\n", err) return } // 对比生成的哈希和原哈希是否一致(使用strings.Compare避免时序攻击) if strings.Compare(newHash, shadowHash) == 0 { fmt.Println("密码验证通过") } else { fmt.Println("密码验证失败") } }
说明
- 依赖安装:先执行
go get golang.org/x/crypto/crypt/sha512_crypt安装依赖包 - 避免时序攻击:使用
strings.Compare而非直接==对比哈希,防止攻击者通过耗时差异破解密码 - 标准算法实现:
sha512_crypt包严格遵循Unix SHA512-crypt的算法规范,确保生成的哈希和passwd命令生成的一致
内容的提问来源于stack exchange,提问作者Subhash Prajapati
相关产品推荐
相关产品推荐

