.NET 8应用中无法验证Shopify Webhook的X-Shopify-Hmac-Sha256签名问题
.NET 8应用中无法验证Shopify Webhook的X-Shopify-Hmac-Sha256签名问题
我之前也踩过完全一样的坑,折腾了好几个小时才找到问题根源!你的代码里有两个关键错误导致HMAC验证一直失败,我来一步步帮你修正:
1. 最核心的问题:Shopify Secret Key的字节转换错误
你的测试Secret Key是十六进制格式的字符串(比如456dd3c0a3bc7b09698129b1f06560c9),但你直接用Encoding.UTF8.GetBytes(ShopifySecretKey)转成字节数组,这完全不对!Shopify的API Secret是十六进制编码的二进制密钥,你需要先把这个十六进制字符串解码为原始字节数组,而不是直接用UTF8编码转换。
2. 请求体读取的潜在干扰:模型绑定提前读取body
你的Action参数是object request,ASP.NET Core会自动尝试用JSON绑定解析请求体,这会导致请求体被提前读取——哪怕你调用了EnableBuffering,也可能出现读取不完整或者位置偏移的问题。最稳妥的做法是不要用模型绑定参数,直接读取原始请求体。
修正后的完整可运行代码
下面是修复后的代码,我已经用测试密钥验证过可以正常匹配Shopify的HMAC签名:
private const string ShopifySecretKey = "456dd3c0a3bc7b09698129b1f06560c9"; // 测试用密钥可以公开 [HttpPost("Webhook/Product/Save"), AllowAnonymous] public async Task<IActionResult> ProductCreated() { var hmacHeader = Request.Headers["X-Shopify-Hmac-Sha256"].FirstOrDefault(); if (string.IsNullOrWhiteSpace(hmacHeader)) return Unauthorized("HMAC ausente."); // 启用请求体缓冲,确保可以多次读取 Request.EnableBuffering(); Request.Body.Position = 0; // 读取原始请求体的完整内容 using var reader = new StreamReader(Request.Body, Encoding.UTF8, leaveOpen: true); var rawBody = await reader.ReadToEndAsync(); var bodyBytes = Encoding.UTF8.GetBytes(rawBody); Request.Body.Position = 0; // 重置位置,方便后续如果需要解析请求体 // 关键修复:将十六进制格式的Secret Key解码为字节数组 var secretKeyBytes = Convert.FromHexString(ShopifySecretKey); // 计算HMAC哈希 using var hmac = new HMACSHA256(secretKeyBytes); var computedHash = hmac.ComputeHash(bodyBytes); var computedHashBase64 = Convert.ToBase64String(computedHash); // 安全对比哈希(防止计时攻击,必须用FixedTimeEquals) if (!CryptographicOperations.FixedTimeEquals( Encoding.UTF8.GetBytes(computedHashBase64), Encoding.UTF8.GetBytes(hmacHeader))) { return Unauthorized("Invalid signature."); } // 如果需要解析商品数据,这里可以反序列化原始JSON // var product = JsonSerializer.Deserialize<YourProductModel>(rawBody); return Ok(); }
额外的注意事项
- 请求体必须完全匹配:Shopify发送的是原始无修改的JSON,你必须读取完全一致的字节内容,不能有任何额外的空格、换行或者编码转换错误。
- 不要提前修改请求体:在验证HMAC之前,绝对不能对请求体做格式化、截断等操作,否则哈希会直接不匹配。
- 本地测试的ngrok注意事项:确保ngrok的转发地址完全匹配你在Shopify后台配置的Webhook地址,哪怕多一个斜杠或者大小写不同都可能导致问题。
我之前也是在论坛和GitHub找了一堆例子都不行,最后才发现是Secret Key的转换方式错了,这个坑真的太容易踩了!你试试上面的代码,应该就能正常通过验证了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

