普通哈希留下的缺口
密码学哈希证明完整性:若摘要仍然相符,数据未被改动。但它丝毫不能证明是谁产生了它。任何人都能哈希一条消息,因此随消息一同发送的摘要,对一个仅仅是改动消息并重新计算摘要的攻击者毫无防护。其中没有秘密参与,因此没有任何只有合法发送者才能做到的事。
弥合了这个缺口。它是一种消息认证码:一个证明消息既未被篡改、又来自持有共享秘密密钥之人的值。验证者用相同的密钥重新计算 HMAC;若相符,消息即为真实。
添加密钥的正确方式
显而易见的想法,只是把密钥与消息合在一起哈希为 hash(密钥 + 消息),是一种隐蔽的不安全。许多哈希函数(包括 系列)建立在一种易受长度扩展攻击影响的构造之上:知道 hash(密钥 + 消息) 与秘密的长度,攻击者无需知道密钥,便能为 密钥 + 消息 + 额外 计算出一个有效哈希。这会让他伪造一条经过扩展、仍然有效的消息。
HMAC(在 RFC 2104 中定义)通过哈希两次来避免这一点,每次以不同方式把密钥混入。从概念上它计算:
HMAC(K, m) = H( (K XOR opad) + H( (K XOR ipad) + m ) )
其中 ipad 与 opad 是两个固定的填充常量。内层哈希把密钥绑定到消息;外层哈希包裹结果,使长度扩展的把戏触及不到它。你不必背下这个公式,要点在于:使 HMAC 稳固的,是这种嵌套结构,而非随意的拼接。
HMAC 与数字签名
两者都证明真实性,但信任模型不同:
- HMAC 是对称的。 一个秘密密钥由双方共享。能验证 HMAC 的人也能创建一个,因此当双方已共享一个密钥、但无法向第三方证明作者身份时,它适用。
- 数字签名是非对称的。 签名者使用私钥;任何人都能用公钥验证。这向全世界证明作者身份,但计算代价更高。
当两个系统共享一个秘密并需要快速的相互认证时,选择 HMAC;当验证者必须无法伪造他们所验证的内容时,选择签名。
你在哪里遇到 HMAC
- API 请求签名。 像 Signature Version 4 这样的方案用 HMAC-SHA256 为每个请求签名,使服务器能确认调用者持有秘密密钥。
- Webhook。 提供方在一个头部中发送有效载荷的 HMAC;你的端点重新计算它以拒绝伪造的调用。
- 使用 的 。 用 HS256 签名的令牌,字面上就是对令牌的头部和有效载荷做 HMAC-SHA256。HMAC 工具 使用与 JWT 验证器完全相同的 Web Crypto 构造,因此二者按设计相符。
你的消息和密钥从不离开浏览器;HMAC 在本地计算。