首页/安全/加密与证书速查/HMAC 与「哈希拼接密钥」的区别
加密算法基础95 条

HMAC 与「哈希拼接密钥」的区别

直接用 H(key || message) 校验消息存在长度扩展风险;HMAC 的双层结构(ipad/opad)避免了该问题,是标准的密钥化哈希构造。

加密与证书速查安全更新于 2026-10-13

HMAC 与拼接写法的区别

HMAC 是 RFC 2104 定义的密钥化哈希构造,内部由两层组成:内层把密钥与 ipad 异或后哈希消息,外层再用 opad 做一次哈希。之所以不能直接用「哈希(密钥 加 消息)」来校验消息来源,是因为 MD5、SHA-1、SHA-256 这类迭代哈希存在长度扩展性质,攻击者可以在已知摘要的前提下追加数据并算出合法摘要。HMAC 的双层结构正好阻断这一点。

基本信息

反例sha256(secret + message),可被长度扩展利用
正例HMAC-SHA256(RFC 2104 构造)
天然替代SHA-3 与 BLAKE2 的海绵结构本身抗长度扩展
比较方式使用 hmac.compare_digest 做常量时间校验
密钥长度任意长度均可,过短会削弱安全性
典型应用Webhook 签名、接口鉴权、会话令牌

典型使用场景

  • 接收 Webhook:按平台文档构造待签串并用 HMAC 校验来源
  • 接口鉴权:用共享密钥对请求参数签名,防止中间篡改
  • 一次性令牌:对数据附加 HMAC 作为完整性标记
  • 代码迁移:把历史代码里拼接密钥的写法替换为 HMAC
  • 对接排查:确认待签串的字段顺序与编码与文档完全一致

正反例对照(Python)

正反例对照(Python)
import hashlib, hmac
# 反例:不要这样校验来源,存在长度扩展风险
bad = hashlib.sha256(secret + msg).hexdigest()
# 正例:HMAC 双层结构,长度扩展不适用
good = hmac.new(secret, msg, hashlib.sha256).hexdigest()
# 比较必须用常量时间函数,避免通过响应耗时逐字节猜签名
ok = hmac.compare_digest(good, received)

构造对照

构造抗长度扩展评价
哈希(密钥 与 消息 拼接)否不要用于来源校验
哈希(消息 与 密钥 拼接)否同样不规范
HMAC是标准做法,生态支持好
带密钥的 SHA-3是海绵结构天然免疫
许多 Webhook 与开放平台使用 HMAC-SHA256 作为签名算法,对接时应严格按文档构造待签串并做常量时间比较。
比较签名的代码必须使用常量时间函数:普通字符串相等比较会提前返回,攻击者可以据此逐字节试探出正确签名。
密钥管理同样关键:Webhook 密钥应当支持轮换,并留出同时接受新旧两个密钥的过渡期,避免换密钥时业务中断。

易错点

  • 把密钥放消息后面(哈希(消息 与 密钥))也不规范,应统一使用 HMAC
  • 拼接 JSON 做签名串时字段顺序与空格必须与文档一致,否则校验必然失败
  • 用普通相等比较校验签名,会泄露前缀信息
  • 重复使用同一密钥且从不轮换,泄露后无法快速止损
  • 待签串里混入未规范化的时间戳或编码差异,导致校验随机失败

数据来源与参考

  • RFC 2104 §2(HMAC 设计动机)
  • FIPS 198-1
​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌