如何使用 HMAC 保护 Webhook 端点

294 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第15天,点击查看活动详情

了解如何使用 HMAC 保护数据,确保只有授权系统才能将数据发送到 webhook 端点。

Webhooks 在SaaS 集成中无处不在,它们是一种简单而快速的方法,可以根据系统中数据的变化,通过 HTTP 回调在系统之间传输数据。 希望通过这篇文章为你之后的使用奠定一些基础。

Webhook 是如何工作的?

简而言之,源应用程序有一个webhook,目标应用程序有一个 webhook 端点;基于源应用程序中发生的某些事件,webhook 将 HTTP 请求发送到 webhook 端点。

下面是一个 HTTP 请求正文(或负载)的简单示例:

{
  "event": "WAREHOUSE_UPDATE",
  "updates": [
    {
      "item": "gadgets",
      "action": "add",
      "quantity": 20
    },
    {
      "item": "widgets",
      "action": "remove",
      "quantity": 10
    }
  ]
}

那么,你要如何确保目标应用程序从源应用程序接收有效数据,而不是从欺骗 webhook 的“坏人”那里接收虚假数据?

简单来说就是,你需要设置 webhook 以向端点提供 HTTP 请求和端点可用于验证数据的唯一密钥。但是,在深入细节之前,需要简单介绍一下HASH。

什么是HASH?

简单来说,HASH是将一个值(或键)转换为另一个值的过程。即使你以前没有使用HASH,你也可能知道 MD5、SHA-256 或 RipeMD-128。其中每一个都是哈希算法 (又名加密哈希函数)的名称。

让我们看看每种算法对经典字符串的作用:

  • MD5Hello World 散列为 ed076287532e86365e841e92bfc50d8c
  • SHA-256 将 Hello World! 散列为 7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
  • RipeMD-128 将 Hello World! 散列为  24e23e5c25bc06c8aa43b696c1e11669 

重要的部分是算法每次都以相同的方式散列。如果我们不更改字符串('Hello World!'),则生成的哈希值也不会更改。但是,如果字符串中的任何内容发生变化,哈希值也会发生变化。例如,让我们将“H”小写,这样我们就有了“hello World!” 看看会发生什么变化:

  • MD5hello World 散列为 41d0c351efedf7fdb9a5dc8a1ed4b4e3
  • SHA-256 将 hello World! 散列为 e4ad0102dc2523443333d808b91a989b71c2439d7362aca6538d49f76baaa5ca
  • RipeMD-128 将 hello World! 散列为  b5cf338f17d6796ba0312e0d78c70831 

尽管只是细微的变化,但由此产生的差异是显而易见的。

虽然HASH不能解决我们最初的问题(向 webhook 端点发送虚假数据),但它确实将我们直接引向了 HMAC

什么是 HMAC?

HMAC,即哈希消息认证码,是一种使用两个密钥而不是一个密钥的认证方法。第一个密钥是HTTP请求体,而第二个密钥是一个秘密加密密钥。当你实现HMAC为你的webhook,你将使用这两个密钥加上算法,如MD5, SHA-256,或RipeMD-128,以确保HTTP请求显示在你的webhook端点是合法的。

HMAC 是如何工作的?

在源应用程序通过webhook发送 HTTP 请求之前,它使用密钥通过 HMAC 对有效负载(请求主体)进行哈希处理。然后将生成的哈希作为标头捆绑到 HTTP 请求中,并将整个请求(标头和正文)发送到 webhook 端点。

收到 HTTP 请求后,目标应用程序使用密钥对主体进行哈希处理,然后将结果与标头中提供的哈希值进行比较。如果值匹配,目标应用程序就会知道数据是合法的并对其进行处理。如果值不匹配,目标应用程序将拒绝数据并执行为该场景编写的任何代码——可能创建日志条目或发送通知。

如果有人试图欺骗负载,他们将无法生成有效的哈希值,因为他们没有密钥,就会被拒绝。

假设你个电子商务平台连接到你用程序。应用会定期将有效负载发送到平台的 webhook 端点以创建订单和发放退款。使用 HMAC 可确保不会有随机(或不太随机)的人向电子商务平台发送虚假订单或退款。

但是,难道不能有人捕获 HTTP 请求并对标头中的哈希进行逆向工程以找出密钥吗?简短的回答:没有。HASH是一种单向函数,要破解具有足够复杂的HASH,所需的计算能力和时间一定比想象的要多。

HMAC 具有广泛的语言支持

你几乎可以使用任何现代语言来计算 HMAC 哈希值。以下是具有 HMAC 功能的流行语言的一些链接:

HMAC 的示例代码

最后,如果没有代码,这一切会怎样?下面是一个示例,说明如何使用内置的加密模块在 NodeJS 中进行设置:

const crypto = require("crypto");

const SECRET_KEY = "secret-FA782CF7-060E-484E-B3DC-055CF2C9ED99";

const payload = JSON.stringify({
  event: "REFUND_REQUEST",
  user: "realcustomer@notabaddie.com",
  amount: "50.25",
});

const hash = crypto
  .createHmac("sha256", SECRET_KEY)
  .update(payload, "utf-8")
  .digest("hex");

console.log(hash); // Prints d12f95e3f98240cff00b2743160455fdf70cb8d431db2981a9af8414fc4ad5f8

使用 HMAC 的相应 HTTP 请求可能如下所示:

curl https://my.webhook.endpoint.com/callback \
  --request POST \
  --header "x-hmac-hash: d12f95e3f98240cff00b2743160455fdf70cb8d431db2981a9af8414fc4ad5f8" \
  --data '{"event":"REFUND_REQUEST","user":"realcustomer@notabaddie.com","amount":"50.25"}'

即使坏人截获了你的 HTTP 请求,他们也无法向自己的电子邮件地址发出退款请求,因为如果没有密钥,他们将无法正确签署请求。

结论

使用 HMAC 不需要你学习一门新语言或深入了解加密,但它确实可以让你保护通过 webhook 传输的数据的完整性。