如何验证Moralis Streams API的x-signature请求头?
Moralis Streams Webhook x-signature 验证失败问题解决
问题背景
在处理Moralis Streams API的入站Webhook请求时,按照官方文档说明,x-signature是请求体与API密钥拼接后的SHA3-256哈希值,但C#实现的签名验证始终不匹配。现有代码直接读取原始请求体拼接密钥后计算哈希,但生成的签名与请求头中的值长度一致却不匹配。从官方JS SDK中发现签名计算逻辑为:
const generatedSignature = sha3(JSON.stringify(body) + secret);
核心原因
Moralis Streams服务端基于Node.js实现,生成x-signature时依赖JavaScript标准的JSON.stringify()序列化规则,而非原始请求体的字节内容。JSON.stringify()有特定的格式要求:
- 默认无缩进、无多余空格
- 字符串转义规则与C#默认JSON序列化存在差异
- 键的顺序遵循对象属性的插入顺序(与C#中某些序列化器的排序逻辑不同)
直接读取原始请求体拼接密钥的方式,无法保证和JSON.stringify(body)的输出完全一致,导致最终哈希结果不匹配。
正确的C#验证实现
要匹配Moralis的签名逻辑,需模拟JSON.stringify()的行为处理请求体,再计算哈希。以下是修正后的代码:
1. 核心验证逻辑
var signature = Request.Headers["x-signature"].FirstOrDefault(); if (string.IsNullOrEmpty(signature)) { logger.LogInformation("No signature was present from webhook request, ignoring."); return Ok(); } Request.EnableBuffering(); // 读取原始请求体 using var reader = new StreamReader(Request.Body, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, leaveOpen: true); var rawBody = await reader.ReadToEndAsync(); Request.Body.Position = 0; try { // 反序列化为JObject(兼容任意结构的Webhook payload) var bodyObject = JsonSerializer.Deserialize<JObject>(rawBody); // 配置序列化选项,匹配JSON.stringify的行为 var jsSerializeOptions = new JsonSerializerOptions { WriteIndented = false, // 禁用缩进,对应JSON.stringify默认行为 Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping, // 避免额外字符转义 PropertyNamingPolicy = JsonNamingPolicy.CamelCase, // 匹配JS的驼峰命名(根据实际payload调整) DictionaryKeyPolicy = JsonNamingPolicy.CamelCase }; // 重新序列化为符合JS格式的JSON字符串 var jsCompatibleJson = JsonSerializer.Serialize(bodyObject, jsSerializeOptions); // 拼接Streams密钥 var dataToHash = jsCompatibleJson + "<你的Streams Secret>"; // 计算SHA3-256哈希 var hashBytes = SHA3_256.HashData(Encoding.UTF8.GetBytes(dataToHash)); var computedSignature = "0x" + Convert.ToHexString(hashBytes).ToLower(); // 验证签名 if (signature != computedSignature) { logger.LogInformation("Signature was incorrect from webhook, ignoring"); return Ok(); } } catch (JsonException ex) { logger.LogError(ex, "Failed to parse webhook body"); return Ok(); } // 签名验证通过,继续处理业务逻辑
2. 注意事项
- 确保
JsonSerializerOptions的配置与你的Webhook payload结构匹配,比如如果payload的键是蛇形命名,需调整PropertyNamingPolicy为null - 若使用.NET版本低于5.0,
Convert.ToHexString可替换为你原有的BytesToHex方法,注意保持十六进制字符为小写 - 必须使用SHA3-256哈希算法,不要混淆为SHA256或其他哈希类型
内容的提问来源于stack exchange,提问作者Ethan
相关产品推荐
相关产品推荐

