EN

专业术语 · 概念

HMAC:基于哈希的消息认证码,用途、密钥与签名比较

依据 IETF RFC 2104 说明 HMAC 是用共享密钥与哈希函数为消息生成认证标签的机制,写明构造公式、密钥要求、RFC 未涉及的时序比较问题,并结合 Shopify webhook 验签说明为何必须用原始请求体、保密密钥与恒定时间比较。

缩写
HMAC
领域
security · web-development

HMAC 是 Hash-based Message Authentication Code(基于哈希的消息认证码)的缩写,由 IETF 在 RFC 2104(1997 年 2 月,状态 Informational)中定义。摘要写明:HMAC 可以配合任何迭代型密码学哈希函数(例如 MD5、SHA-1)与一个秘密的共享密钥使用(核验于 2026-09-29)。

用途

RFC 引言把这类机制称为消息认证码(MAC):通常用在共享同一个秘密密钥的双方之间,用来验证二者之间传输的信息。接收方用同一个密钥重新计算标签,与收到的标签一致,才认为消息未被篡改、且来自持有密钥的一方。它解决“这条消息是否来自持有密钥的人、内容是否被改过”,不解决保密:HMAC 不加密消息内容。

怎么算

RFC 的公式:H(K XOR opad, H(K XOR ipad, text))。H 是哈希函数,K 是密钥,B 是哈希的分块长度(MD5 与 SHA-1 为 64 字节),L 是哈希输出长度(MD5 为 16,SHA-1 为 20 字节),ipad 是字节 0x36 重复 B 次,opad 是字节 0x5C 重复 B 次。日常使用直接调用语言或库提供的 HMAC,不需要自己实现。

密钥与安全性

  • RFC 第 3 节写明密钥应随机选取(或由密码学强度的伪随机生成器加随机种子产生),并周期性更新;推荐的最小密钥长度为 L 字节;长于 B 字节的密钥会先用 H 哈希,再把结果作为实际密钥;
  • 第 6 节说明,已知对 HMAC 最强的攻击基于哈希函数的碰撞频率(生日攻击),攻击者需要拿到用同一个秘密密钥计算的大量已知消息的正确标签(文中量级约 2 的 64 次方);
  • RFC 没有逐字写“密钥泄露的后果”。按 MAC 的定义,持有密钥的人就能生成有效标签,因此密钥必须只由通信双方持有,不进入前端代码、日志或仓库,这是据定义的推论,不是 RFC 的原文。
  • 截断输出(第 5 节):可以只取标签最左的 t 位,RFC 建议 t 不少于哈希输出长度的一半,且不少于 80 位。

验证标签时要恒定时间比较

RFC 2104 全文没有讨论时序(timing)问题。这一条依据两份其他官方文档:

  • Python 官方文档称,hmac.compare_digest() 的做法旨在避免基于内容的短路行为,以防时序分析;在把 digest() 的输出与外部提供的摘要比较时,建议用它而不是 ==。文档也说明,两者长度不同时,时序攻击理论上可能泄露类型与长度的信息,但不泄露值;
  • Shopify 的 webhook 验签页要求:用应用的 client secret 对原始请求体计算 HMAC-SHA256,与 X-Shopify-Hmac-SHA256 头里的 base64 签名比较;页面的代码示例使用 crypto.timingSafeEqual(),并强调验签需要原始请求体,不能先解析再验。

Shopify 一侧的完整流程、密钥轮换与重试见 Webhooks 与事件。

与相邻概念的边界

相邻概念边界
普通哈希(如 SHA-256)没有密钥,任何人都能重算;HMAC 需要共享密钥
数字签名使用非对称密钥,验证方不持有签名私钥;HMAC 双方共享同一密钥。数字签名的细节本文未读
加密保护内容不被读取;HMAC 只提供完整性与来源验证
用户密码存储不属于 HMAC 的用途,本文所读来源未涉及

常见误用

  • 用 == 直接比较签名字符串,忽略上文两份官方文档给出的时序风险提示;
  • 在验签前用框架解析请求体,再用重新序列化的内容计算,导致与发送方所签的字节不一致;
  • 把密钥写进前端或提交到仓库;
  • 认为 HMAC 能防重放:RFC 2104 的定义不含时间戳或去重,重复投递需另行处理,Shopify 一侧的去重见上文链接。

参考资料

内容维护: WooDawn

资料核对:2026-09-29 · 部分核对,适用边界见正文

2026-09-29 读取 IETF RFC 2104(1997 年 2 月,Informational)摘要、引言、第 3 节密钥、第 5 节截断输出与第 6 节安全性;该 RFC 全文不含 timing 或 constant time 字样,恒定时间比较的依据改读 shopify.dev 的 webhook HTTPS 投递验签页与 Python 官方文档 hmac 模块页;RFC 2104 原文以 MD5 与 SHA-1 为例,SHA-256 的使用依据 Shopify 页面;未读取 NIST FIPS 198-1 与 RFC 4231 等后续规范,也未逐行核对 RFC 的全部第 2 节参考实现。

内容更新:2026-09-29