Zoho 订阅验证 webhook 签名 NodeJS

Zoho subscription validate webhook signature NodeJS

我正在尝试保护我的 Zoho webhook 实施。我遵循了这个文档:https://www.zoho.com/subscriptions/kb/webhooks/securing-webhooks.html

我不太清楚该怎么做,但我很确定最后我还是按照他们说的去做了。

我没有任何查询参数。 格式只是默认 JSON 有效负载 NO X-WWW-FORM-URLENCODED.

我尝试使用以下代码,但没有得到正确的哈希值。还不清楚我是否应该对默认负载进行排序。根据 this 回答,只有表单 url 编码和查询参数是必需的,但对于普通 JSON 有效负载不需要处理。无论哪种方式,我都尝试了以下实现方式:

function computeZohoSignature(query, payload) {
    return crypto
         .createHmac('sha256', process.env.ZOHO_WEBHOOK_SECRET)
         .update(JSON.stringify(payload), 'utf8')
         .digest('hex');
}

function validSignature(signatureHash, computedHash) {
  return signatureHash.length === computedHash.length
     && crypto.timingSafeEqual(Buffer.from(signatureHash), Buffer.from(computedHash));
} 

我还尝试使用以下函数包装有效载荷:

function sortObjectByKeys(object) {
  if (!isObject(object)) return object;

  const sortedObj = {};
  Object
    .keys(object)
    .sort()
    .forEach((k) => {
      sortedObj[k] = sortObjectByKeys(object[k]);
    });

  return sortedObj;
}

排序正确,我什至尝试只对 "root-keys" 进行排序。不管我尝试什么,哈希值都不一样。是的,我 100% 确定这个秘密是正确的,我检查了三次。

有没有人看到这里出了什么问题,或者有一个有效的 NodeJS 实现来执行此操作?

提前致谢!

典型的 Express 设置使用以下配置进行解析:

app.use(bodyParser.json());

此解析器会将请求的 body 的 parsed (object) 内容添加到请求的 body 属性路由处理程序的第一个 req 参数 (req, res) => { ... }.

但是,您的 webhook 的哈希值是根据 raw(字符串)有效负载计算的。虽然您可以使用 JSON.stringify 将已解析的 body 转换回字符串,但这可能会导致与原始原始负载不一致。

例如,如果您的货币是 Euro,Zoho 将传递编码的 "\u20a" 作为 currency_symbol 的值。但是,如果您使用 JSON.stringify 将已解析的 body 转换回字符串,您会发现它生成的是未编码的 "€"。因为这导致两个字符串不相同,所以它们不会产生相同的散列。

如果不直接访问原始 body,很难准确地检测到原始原始 body 与您的 JSON.stringify 结果之间的区别,以及如何后者应该转换为与原始原始 body 相同的格式。克服这个问题的最简单方法是使用解析器的 verify 方法,将原始 body 添加到例如。路由处理程序的第一个 req 参数的 rawBody 属性,如 in this article 所述:

app.use(bodyParser.json({
  verify: (req, res, buf) => {
    req.rawBody = buf
  }
}))

如果您将 req.rawBody 的值分配给 payload,您的 computeZohoSignature 方法生成的散列现在应该与 Zoho!

传递的签名相对应