You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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篡改问题

你的思路完全错误,核心原因如下:

  1. nacl/sign生成的令牌格式是[64字节签名] + [原始消息],而非你认为的消息+公钥。
  2. 签名由私钥生成,仅靠公钥无法伪造有效签名——这是数字签名的核心安全特性,没有私钥的话,无法生成"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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 22:00:58