开启掘金成长之旅!这是我参与「掘金日新计划 · 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。其中每一个都是哈希算法 (又名加密哈希函数)的名称。
让我们看看每种算法对经典字符串的作用:
- MD5 将
Hello World散列为ed076287532e86365e841e92bfc50d8c - SHA-256 将
Hello World!散列为7f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069 - RipeMD-128 将
Hello World!散列为24e23e5c25bc06c8aa43b696c1e11669
重要的部分是算法每次都以相同的方式散列。如果我们不更改字符串('Hello World!'),则生成的哈希值也不会更改。但是,如果字符串中的任何内容发生变化,哈希值也会发生变化。例如,让我们将“H”小写,这样我们就有了“hello World!” 看看会发生什么变化:
- MD5 将
hello 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 传输的数据的完整性。