在C#中验证Zendesk Webhook签名有效性遇阻求助
Zendesk Webhook签名验证C#代码修复方案
尝试用C#验证Zendesk Webhook签名有效性但代码无法运行,原代码如下:
string json = await new StreamReader(HttpContext.Request.Body).ReadToEndAsync(); string signature = Request.Headers["x-zendesk-webhook-signature"]; string timestamp = Request.Headers["x-zendesk-webhook-signature-timestamp"]; // Signing secret from webhook itself string SIGNING_SECRET = "Secret key"; _logger.LogDebug("Zendesk signature: " + signature); _logger.LogDebug("Zendesk timestamp: " + timestamp); _logger.LogDebug("Zendesk body: " + json); var b1 = timestamp + json; string newSignature = ""; using (var hmacsha256 = new HMACSHA256(Encoding.UTF8.GetBytes(SIGNING_SECRET))) { var hash = hmacsha256.ComputeHash(Encoding.UTF8.GetBytes(b1)); newSignature = Convert.ToBase64String(hash); } return signature.Equals(newSignature, StringComparison.OrdinalIgnoreCase);
参考的Zendesk官方验证逻辑(Node.js版本文档):将请求头中的时间戳与原始请求体拼接,使用Webhook的签名密钥通过HMAC-SHA256算法生成哈希值,再转换为Base64编码,最后与请求头中的签名值比对,同时需验证时间戳防止重放攻击。
问题排查与修复点:
- 请求体读取与编码:原代码未指定StreamReader编码,可能导致请求体字符串编码异常;且读取后未重置流位置,会影响后续对请求体的操作。
- 时间戳验证缺失:未检查时间戳的有效性,存在重放攻击风险,也不符合官方安全要求。
- 签名比对逻辑:Base64编码大小写敏感,原代码用
OrdinalIgnoreCase比对可能导致不匹配。 - 请求体完整性:确保读取的是原始未修改的请求体,不要对JSON做格式化、缩进等操作,否则会导致哈希值不匹配。
修复后的代码:
using System.Security.Cryptography; using System.Text; // 以UTF-8编码读取原始请求体,确保与Zendesk发送的内容一致 var rawBody = await new StreamReader(HttpContext.Request.Body, Encoding.UTF8).ReadToEndAsync(); // 重置请求体流位置,避免后续处理请求体时出错 HttpContext.Request.Body.Position = 0; string receivedSignature = Request.Headers["x-zendesk-webhook-signature"]; string receivedTimestamp = Request.Headers["x-zendesk-webhook-signature-timestamp"]; // 从Zendesk Webhook设置中获取的签名密钥 string signingSecret = "你的签名密钥"; // 1. 验证时间戳有效性,防止重放攻击 if (!long.TryParse(receivedTimestamp, out long timestamp)) { return false; } var currentUnixTime = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); // 允许5分钟的时间误差,可根据需求调整 if (Math.Abs(currentUnixTime - timestamp) > 300) { return false; } // 2. 拼接时间戳与原始请求体 var dataToSign = receivedTimestamp + rawBody; // 3. 生成HMAC-SHA256哈希并转为Base64 string computedSignature; using (var hmac = new HMACSHA256(Encoding.UTF8.GetBytes(signingSecret))) { var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(dataToSign)); computedSignature = Convert.ToBase64String(hashBytes); } // 4. 严格比对签名(Base64大小写敏感) return computedSignature.Equals(receivedSignature, StringComparison.Ordinal);
内容的提问来源于stack exchange,提问作者Hardik Masalawala
相关产品推荐
相关产品推荐

