HMAC 签名计算
用密钥计算 HMAC-SHA256 签名,接口调试常用
HMAC vs 裸哈希:为什么不能直接 hash(secret + message)
HMAC 解决的问题
裸哈希只能证明"内容没变",不能证明"内容是谁发的"。一个直觉的补救办法是把密钥拼在前面再哈希:hash(secret + message)。这个做法是不安全的,原因是长度扩展攻击(length extension attack)。
MD5、SHA-1、SHA-256 这些算法都属于 Merkle–Damgård 结构:处理完最后一块数据后,内部状态就是输出。攻击者拿到 hash(secret + message) 就等于拿到了内部状态,可以直接从这个状态继续往下算,构造出 hash(secret + message + padding + 追加内容) 却不需知道 secret。于是他能伪造出一条服务端会认可的报文。
HMAC 的构造
HMAC(RFC 2104)用两次哈希和两个派生密钥把这个缺口堵住:
HMAC(K, m) = H( (K ⊕ opad) ‖ H( (K ⊕ ipad) ‖ m ) )
外层再哈希一次,意味着最终输出不是一个可以被继续扩展的状态,攻击者无法从输出反推中间状态。密钥按块大小补齐后与固定常量异或,得到内外两个不同的密钥。
结果:即使底层哈希被发现有碰撞问题,HMAC 的安全性也不会立刻崩塌——这也是 HMAC-MD5 至今在某些协议里仍被认为可用、而裸 MD5 早已不可用的原因。
和数字签名的区别
| HMAC | 数字签名(RSA/ECDSA) | |
|---|---|---|
| 密钥 | 对称,双方共享同一个 | 非对称,私钥签、公钥验 |
| 验证方能否伪造 | 能(它也有密钥) | 不能 |
| 速度 | 快 | 慢几个数量级 |
| 适用 | API 请求签名、Webhook 校验 | 需要不可否认性的场景 |
关键取舍:用 HMAC 时,能验证签名的一方也就能生成签名。所以它适合"我和我的服务端"之间,不适合"我需要对第三方证明这条消息来自我"。
实际使用要点
- 密钥长度:HMAC-SHA256 的密钥建议至少 32 字节的随机值,别用短口令。
- 比较用时间恒定算法。逐字符比较会在第一个不同的字符处提前返回,攻击者可以通过测量响应时间逐字节猜出正确签名。要用
crypto.timingSafeEqual一类的时间恒定比较。 - 把时间戳纳入被签名内容。否则签名可以被无限期重放。服务端校验时间窗口(如 ±5 分钟)。
- 签名的内容要包含全部关键字段,包括路径、方法、body 摘要,漏签一个字段就等于给了篡改入口。
本工具支持 HMAC-MD5 / SHA-1 / SHA-256 / SHA-512,输出十六进制或 Base64。计算在浏览器本地完成,密钥和消息都不会离开你的设备。
常见问题
HMAC 用来做什么?
验证消息未被篡改且来自持有同一密钥的一方。常见于 Webhook 验签(如飞书、GitHub、微信支付)和 API 签名。