Golang替换随机公钥为常量公钥及Cookie篡改实现求助
一、解决常量密钥对替换与类型适配问题
你的代码核心有两个错误:一是sign.Sign需要私钥而非公钥签名;二是直接把字符串转成[]byte无法适配nacl要求的固定大小数组指针类型。以下是修正后的代码:
package main import ( "encoding/base64" "encoding/json" "log" "net/http" "os" "github.com/gin-gonic/gin" "golang.org/x/crypto/nacl/sign" ) type user struct { Username string `json:"username"` Password string `json:"password"` } var users []user // 定义常量密钥对(注意:私钥要保密,不要硬编码到生产代码里) const ( privkeyStr = "替换成你的base64 RawURL编码的32字节私钥" pubkeyStr = "123eRhvE0bqx81t3G9o79LOkkR4CK9Cyhvt8-QJ-s1H2A6uSwukUI0CYUlc3WbmIAa3VTo4Jm9RF5F0Af4DzmQ" ) var privkey *[32]byte var pubkey *[64]byte func init() { // 解码私钥并转换为*[32]byte privBytes, err := base64.RawURLEncoding.DecodeString(privkeyStr) if err != nil { log.Fatal("私钥解码失败:", err) } if len(privBytes) != 32 { log.Fatal("私钥长度必须为32字节") } privkey = new([32]byte) copy(privkey[:], privBytes) // 解码公钥并转换为*[64]byte pubBytes, err := base64.RawURLEncoding.DecodeString(pubkeyStr) if err != nil { log.Fatal("公钥解码失败:", err) } if len(pubBytes) != 64 { log.Fatal("公钥长度必须为64字节") } pubkey = new([64]byte) copy(pubkey[:], pubBytes) } func login(c *gin.Context) { var tryUser user if err := c.BindJSON(&tryUser); err != nil { return } for _, u := range users { if u == tryUser { // 使用私钥签名用户名 signedToken := sign.Sign(nil, []byte(u.Username), privkey) signedTokenStr := base64.RawURLEncoding.EncodeToString(signedToken) c.IndentedJSON(http.StatusOK, gin.H{"cookie": signedTokenStr}) c.IndentedJSON(http.StatusOK, gin.H{"public_key": base64.RawURLEncoding.EncodeToString(pubkey[:])}) return } } c.IndentedJSON(http.StatusForbidden, gin.H{"msg": "用户名或密码错误"}) } func home(c *gin.Context) { c.IndentedJSON(http.StatusOK, gin.H{"public_key": base64.RawURLEncoding.EncodeToString(pubkey[:])}) } func main() { // 读取用户文件 bytes, err := os.ReadFile("users.json") if err != nil { log.Println("无法加载用户文件:", err) return } if err := json.Unmarshal(bytes, &users); err != nil { log.Println("解析用户文件失败:", err) return } router := gin.Default() router.POST("/login", login) router.GET("/", home) if err := router.Run("0.0.0.0:80"); err != nil { log.Fatal("服务启动失败:", err) } }
关键修正点:
- 新增
init函数处理常量密钥的解码和类型转换,适配nacl要求的*[32]byte(私钥)和*[64]byte(公钥)类型。 - 修复
login函数的签名逻辑:用私钥签名,而非公钥(sign.Sign的第三个参数是私钥)。 - 替换已废弃的
ioutil.ReadFile为os.ReadFile。
二、解决Cookie篡改问题
你的思路完全错误,核心原因如下:
- nacl/sign生成的令牌格式是
[64字节签名] + [原始消息],而非你认为的消息+公钥。 - 签名由私钥生成,仅靠公钥无法伪造有效签名——这是数字签名的核心安全特性,没有私钥的话,无法生成"admin"对应的有效Cookie。
如果拥有服务端私钥,正确生成admin有效Cookie的代码如下:
package main import ( "encoding/base64" "fmt" "log" "golang.org/x/crypto/nacl/sign" ) const ( privkeyStr = "替换成你的私钥base64 RawURL编码" ) func main() { // 解码私钥 privBytes, err := base64.RawURLEncoding.DecodeString(privkeyStr) if err != nil { log.Fatal("私钥解码失败:", err) } if len(privBytes) != 32 { log.Fatal("私钥长度必须为32字节") } privkey := new([32]byte) copy(privkey[:], privBytes) // 生成admin对应的有效Cookie targetUsername := "admin" signedToken := sign.Sign(nil, []byte(targetUsername), privkey) adminCookie := base64.RawURLEncoding.EncodeToString(signedToken) fmt.Println("admin的有效Cookie:", adminCookie) }
若没有私钥,仅靠公钥根本无法篡改出有效Cookie——这是数字签名的设计初衷,防止未经授权的内容篡改。你之前的拼接逻辑错误,自然会导致Cookie长度不符。
内容的提问来源于stack exchange,提问作者gnarkill78
相关产品推荐
相关产品推荐

