Go语言HMAC SHA256 Webhook签名验证异常排查求助
Go语言HMAC SHA256 Webhook签名验证异常排查求助
大家好,最近在把Node.js里跑的好好的Webhook签名验证逻辑迁移到Go,但死活生成不对正确的HMAC SHA256签名,已经排查掉了“错误把secret转成hex再传入HMAC”这个坑,但还是不行,想请大家帮忙看看还有哪里可能出问题!
先贴出两边的代码对比:
正常工作的Node.js代码
import crypto from "crypto"; import express from "express"; export const computeWebhookSignature = ({ requestBody, secret, timestamp, }: { requestBody: any; secret: string; timestamp: number; }): string => { // 核心逻辑:拼接时间戳和JSON序列化后的请求体,再计算HMAC const payload = `${timestamp}.${JSON.stringify(requestBody)}`; const hmac = crypto.createHmac("sha256", secret); hmac.update(payload); return hmac.digest("hex"); }; // Webhook接收示例 const app = express(); app.use(express.json()); app.post("/webhook", (req, res) => { const signatureHeader = req.headers["x-webhook-signature"]; const timestamp = parseInt(req.headers["x-webhook-timestamp"] as string); const computedSignature = computeWebhookSignature({ requestBody: req.body, secret: process.env.WEBHOOK_SECRET!, timestamp, }); if (computedSignature === signatureHeader) { res.status(200).send("签名验证通过"); } else { res.status(403).send("签名无效"); } });
验证失败的Go代码
package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "encoding/json" "fmt" "net/http" "strconv" ) func computeWebhookSignature(requestBody interface{}, secret string, timestamp int64) string { // 这里已经修正了之前把secret转hex的错误 bodyBytes, _ := json.Marshal(requestBody) payload := fmt.Sprintf("%d.%s", timestamp, bodyBytes) h := hmac.New(sha256.New, []byte(secret)) h.Write([]byte(payload)) return hex.EncodeToString(h.Sum(nil)) } func webhookHandler(w http.ResponseWriter, r *http.Request) { signatureHeader := r.Header.Get("X-Webhook-Signature") timestampStr := r.Header.Get("X-Webhook-Timestamp") timestamp, _ := strconv.ParseInt(timestampStr, 10, 64) var requestBody interface{} json.NewDecoder(r.Body).Decode(&requestBody) computedSignature := computeWebhookSignature(requestBody, "my-webhook-secret", timestamp) if computedSignature == signatureHeader { w.WriteHeader(http.StatusOK) w.Write([]byte("签名验证通过")) } else { w.WriteHeader(http.StatusForbidden) w.Write([]byte("签名无效")) fmt.Printf("计算的签名:%s,收到的签名:%s\n", computedSignature, signatureHeader) } } func main() { http.HandleFunc("/webhook", webhookHandler) http.ListenAndServe(":8080", nil) }
我现在怀疑几个方向,但不确定:
- JSON序列化差异:Node.js的
JSON.stringify()和Go的json.Marshal()处理对象的细节不一样,比如字段顺序、是否保留空格、空值的处理,会不会导致拼接的payload字符串不一样? - 请求体原始字节的读取:Node.js里
express.json()拿到的req.body是解析后的对象,但Go里用json.NewDecoder读取后,有没有可能丢失了原始请求体的某些字节细节?比如有没有可能应该直接读取原始字节而不是先解析? - 时间戳的字符串格式:Node.js里timestamp是number转字符串,Go里是int64转字符串,会不会有格式差异?比如有没有可能时间戳的位数不对?
- HMAC的输入编码:有没有可能在拼接payload时,字符串转字节的编码不一致?比如Node.js默认是UTF-8,Go里是不是也是用的UTF-8?
有没有大佬遇到过类似的问题,或者能帮我指出代码里的其他潜在问题?感激不尽!
备注:内容来源于stack exchange,提问作者Robert Seares
相关产品推荐
相关产品推荐

