You need to enable JavaScript to run this app. GoLang实现与PHP等效的Shopify HMAC校验不匹配问题排查
优惠活动
大模型
产品
解决方案
定价
更多

GoLang实现与PHP等效的Shopify HMAC校验不匹配问题排查

问题原因

你的Go实现存在三个核心问题,按优先级排序如下:

  1. 低级语法错误
    Go语言中仅支持用单引号包裹单个字符(rune类型),多字符字符串必须用双引号包裹。你代码中创建密钥的写法[]byte('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')本身就会编译失败,首先要修正为[]byte("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")。

  2. 逻辑错误:重复序列化JSON
    这是导致校验失败的最核心原因:你将请求数据反序列化为Go结构体后,又重新用json.Marshal序列化,此时得到的字节流和PHP侧json_encode的结果大概率不一致,原因包括:

  • Go标准库json.Marshal默认会对<、>、&等HTML特殊字符做Unicode转义,而PHP的json_encode默认不开启转义
  • 不同语言对浮点数格式化、空值处理、字段排序的规则存在差异,都会导致序列化后的内容不一致
    正确做法是直接读取请求的原始Body字节流计算HMAC,不要做任何序列化/反序列化操作,避免两边序列化规则差异导致的输入不一致。
  1. 无用逻辑冗余
    Shopify返回的X-Shopify-Hmac-Sha256请求头是base64编码的结果,和你PHP侧的逻辑完全一致,hex编码的校验分支完全无用,可以直接删除。

修正后代码示例

import (
    "crypto/hmac"
    "crypto/sha256"
    "encoding/base64"
    "io"
    "bytes"
    "net/http"
)

// 校验逻辑
func verifyShopifyHMAC(r *http.Request) (bool, error) {
    // 读取原始请求Body
    rawBody, err := io.ReadAll(r.Body)
    if err != nil {
        return false, err
    }
    // 回填Body,避免后续业务逻辑读不到请求数据
    r.Body = io.NopCloser(bytes.NewBuffer(rawBody))

    // 计算HMAC
    secret := []byte("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")
    h := hmac.New(sha256.New, secret)
    h.Write(rawBody)
    calculatedHmac := base64.StdEncoding.EncodeToString(h.Sum(nil))

    // 校验对比,使用hmac.Equal避免时序攻击
    requestHmac := r.Header.Get("X-Shopify-Hmac-Sha256")
    return hmac.Equal([]byte(calculatedHmac), []byte(requestHmac)), nil
}

如果确实需要使用反序列化后的input结构体重新序列化计算,需要禁用Go JSON序列化的HTML转义,对齐PHP的默认行为:

var buf bytes.Buffer
enc := json.NewEncoder(&buf)
enc.SetEscapeHTML(false) // 禁用HTML转义
if err := enc.Encode(input); err != nil {
    // 错误处理
}
// 去掉Encode默认追加的换行符
inputData := bytes.TrimSuffix(buf.Bytes(), []byte("\n"))

内容的提问来源于stack exchange,提问作者user14041681

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 13:06:03