HMAC 生成器

使用 SHA-1、SHA-256、SHA-384 或 SHA-512 为任意消息与密钥计算 HMAC 签名,支持 hex、base64、base64url 输出。

本地计算消息认证码。 HMAC 将哈希函数与密钥结合,使接收方既能校验完整性也能验证真实性。通过 Web Crypto API 在浏览器内完成。

HMAC 到底解决什么问题

它做认证,不做加密

HMAC 不隐藏任何内容。它回答的是另一个问题:这条消息是否由持有共享密钥的人生成,以及在此之后是否被改动过?输出是一个定长标签,随消息一起发送;接收方用相同密钥重新计算并比对。标签一致说明消息真实且完整;不一致则说明两者都不成立,而且无法区分究竟是哪一种。

正因如此,HMAC 是 webhook 签名、API 请求签名、签名 Cookie 和会话令牌背后的主力。Stripe、GitHub 以及大多数 webhook 服务商都用这种方式签名负载,好让你能验证请求确实来自它们,而不是来自某个猜中了你接口地址的人。

为什么不能直接把密钥和消息拼起来做哈希

最直觉的构造 SHA256(密钥 + 消息) 在 SHA-1、SHA-256 这类 Merkle-Damgård 哈希上是不安全的。这类哈希按块处理数据并向后传递内部状态,因此攻击者只要知道一个合法摘要,就能在消息后追加数据并算出扩展消息的合法摘要,全程无需得知密钥。这就是长度扩展攻击,它并非纸上谈兵,已经攻破过真实的 API。

HMAC 的嵌套构造 H(密钥 XOR opad || H(密钥 XOR ipad || 消息)) 堵住了这个漏洞。两轮使用不同填充密钥的运算,使最终摘要不再是"对攻击者可影响数据做哈希"后的裸内部状态。这也是为什么你应当直接使用 HMAC,而不要自创带密钥的哈希方案。

实务要点

除非外部条件另有约束,选 SHA-256。SHA-1 在 HMAC 内部仍然安全,因为该构造并不依赖抗碰撞性,但新系统没有理由继续使用它,而且审计时一定会被标记。SHA-512 在这个用途上并没有实质更强,只是在 64 位硬件上有时更快。

有两个实现细节比选哪个哈希更重要。第一,比较标签必须使用恒定时间比较——普通字符串比较在第一个不同字节处就提前返回,泄露的时间信息足以让攻击者逐字节伪造出标签。第二,密钥应当是长度不小于摘要长度的随机字节,而不是一句好记的口令;HMAC 密钥不是密码哈希,不会经过任何强化处理。

开源说明:使用浏览器原生的 Web Crypto(crypto.subtle)API 实现,不依赖第三方库。

常见问题

我的密钥会被发送出去吗?
不会。密钥和消息始终留在浏览器内,签名通过 Web Crypto API 在本地计算,不会传输也不会存储。
应该选择哪个算法?
新项目一律用 SHA-256。SHA-384 和 SHA-512 能以很小的代价提高安全裕度;SHA-1 只是为了兼容老系统而保留。
base64 和 base64url 有什么区别?
base64url 用 - 和 _ 替换 + 和 /,并去掉了填充字符,因此结果可以直接放进 URL、文件名和 HTTP 头。
HMAC 属于加密吗?
不属于。它只能证明消息出自持有密钥的一方且未被篡改,消息内容本身仍然完全可读。如果还需要保密,请配合 TLS 或加密使用。
为什么不能直接把密钥和消息拼起来做哈希?
普通 SHA-2 存在长度扩展攻击:攻击者无需知道密钥,就能在已签名的消息后追加数据并伪造出有效摘要。HMAC 的嵌套结构堵住了这个漏洞。
代码里应该怎么比较两个 HMAC?
使用常数时间比较,例如 crypto.timingSafeEqual。普通的 == 会在第一个不同字节处提前返回,可能让攻击者逐字节试出正确的签名。