目 录CONTENT

文章目录
Go

go实现Web参数签名验证--hmac-sha256

过客
2025-11-20 / 0 评论 / 0 点赞 / 3 阅读 / 0 字

之前Web应用参数签名验证用MD5比较多,但由于 MD5 存在严重的碰撞漏洞(即不同内容可生成相同哈希值),早已不适合用于安全敏感场景,有很多其他验证参数方式。

方式 安全性 性能 实现难度 密钥管理 适用场景
HMAC-SHA256 ★★★★☆ ★★★★★ ★★☆ 通用 API 签名(推荐首选)
RSA/ECDSA ★★★★★ ★★☆ ★★★★ 金融、高安全合规场景
JWT (HS256/RS256) ★★★★☆ ★★★★☆ ★★★ 中~高 无状态认证、OAuth、移动端
双向 TLS ★★★★★ ★★★☆ ★★★★★ 极高 M2M、IoT、内部高安全系统
  • 大多数 Web API 场景​:优先使用 ​HMAC-SHA256​,搭配时间戳 + nonce 防重放。
  • 需要更强身份隔离或合规要求​(如 PCI-DSS、GDPR):考虑 ​RSA/ECDSA 签名​。
  • 已采用 OAuth 或需要无状态登录​:使用 JWT + RS256 更合适。
  • 内部微服务或设备通信​:可评估 ​双向 TLS​,但需权衡运维成本。

这里讲一下能直接替代MD5的HMAC-SHA256方式。

老规矩,直接上完整Demo代码:
这个实现包含以下功能:

  1. SignParameters​: 使用 HMAC-SHA256 对参数进行签名
  2. VerifySignature​: 验证签名的有效性
  3. apiHandler​: HTTP 处理器,验证请求签名
  4. generateSignedURL​: 示例客户端函数,生成带签名的URL

使用说明:

  1. 修改 secretKey 为实际的密钥
  2. 参数按字典序排序后拼接成查询字符串
  3. 服务端验证时排除 signature 参数本身
  4. 包含防重放机制(通过 timestampnonce
package main

import (
	"crypto/hmac"
	"crypto/sha256"
	"encoding/hex"
	"fmt"
	"net/http"
	"sort"
	"strings"
)

// 用于 HMAC 签名的全局密钥(在生产环境中,从配置文件加载)
var secretKey = []byte("your-secret-key-here")

// SignParameters 根据给定参数生成 HMAC-SHA256 签名
func SignParameters(params map[string]string) string {
	// 按字典顺序按键对参数进行排序
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	// 根据排序后的参数构建查询字符串
	var queryStr string
	for i, k := range keys {
		if i > 0 {
			queryStr += "&"
		}
		queryStr += fmt.Sprintf("%s=%s", k, params[k])
	}

	// 创建 HMAC-SHA256 哈希
	h := hmac.New(sha256.New, secretKey)
	h.Write([]byte(queryStr))
	return hex.EncodeToString(h.Sum(nil))
}

// VerifySignature 验证给定的签名是否与计算出的签名匹配。
func VerifySignature(params map[string]string, expectedSignature string) bool {
	calculatedSignature := SignParameters(params)
	return hmac.Equal([]byte(calculatedSignature), []byte(expectedSignature))
}

// 演示签名验证的 HTTP 处理程序
func apiHandler(w http.ResponseWriter, r *http.Request) {
	// 提取查询参数
	params := make(map[string]string)
	for key, values := range r.URL.Query() {
		if key != "signature" { // 从验证中排除签名参数
			if len(values) > 0 {
				params[key] = values[0] // 如果存在多个值,则取第一个值。
			}
		}
	}

	// 从查询中获取预期签名
	expectedSignature := r.URL.Query().Get("signature")
	if expectedSignature == "" {
		http.Error(w, "Missing signature", http.StatusBadRequest)
		return
	}

	// 验证签名
	if !VerifySignature(params, expectedSignature) {
		http.Error(w, "Invalid signature", http.StatusUnauthorized)
		return
	}

	// 签名有效,处理请求
	fmt.Fprintf(w, "Signature verified successfully!\nReceived parameters: %v", params)
}

// 生成签名 URL 的示例客户端函数
func generateSignedURL(baseURL string, params map[string]string) string {
	signature := SignParameters(params)
	params["signature"] = signature

	// 构建带有签名的最终 URL
	var queryStr string
	keys := make([]string, 0, len(params))
	for k := range params {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	for i, k := range keys {
		if i > 0 {
			queryStr += "&"
		}
		queryStr += fmt.Sprintf("%s=%s", k, params[k])
	}

	return fmt.Sprintf("%s?%s", baseURL, queryStr)
}

func main() {
	// 示例用法:生成签名请求
	clientParams := map[string]string{
		"timestamp": "1678886400",
		"nonce":     "abc123",
		"user_id":   "12345",
		"data":      "example_data",
	}

	signedURL := generateSignedURL("http://localhost:8080/api", clientParams)
	fmt.Println("Generated signed URL:", signedURL)

	// 启动 HTTP 服务器
	http.HandleFunc("/api", apiHandler)
	fmt.Println("Server starting on :8080")
	http.ListenAndServe(":8080", nil)
}
0
Go
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区