dev

Hash vs HMAC:Checksum、文件指纹和 API 签名到底该用哪个

对比 Hash 和 HMAC 在校验和、文件完整性、Webhook 验签和 API 签名中的不同作用。文章说明什么时候只需要摘要,什么时候必须使用带 secret 的 HMAC,以及为什么 hash 不能证明消息来源或防止伪造。并结合文件校验、请求签名和 webhook 校验说明选择边界。

Hash 和 HMAC 的输出都可能是一长串 hex,看起来很像,但它们解决的问题不一样。Hash 适合回答“这份内容有没有变化”;HMAC 适合回答“这份消息是否由持有共享 secret 的一方生成,并且中途没有被改”。如果把普通 hash 当 API 签名,攻击者修改消息后也能重新算一个新 hash。

简单判断:没有 secret 参与的是 hash;有共享 secret 参与、用于认证消息的是 HMAC。可以用 Hash Generator 做摘要对比,用 HMAC Generator 复现签名样例,但两者不能互换。

Hash 适合完整性和指纹,不适合证明身份

Hash 的特点是确定性:同一输入、同一算法,得到同一摘要。它适合这些场景:

  • 下载文件后对比供应商发布的 SHA-256 checksum。
  • 给静态资源生成内容指纹,内容变了 URL 或缓存 key 就变。
  • 对非敏感文本做去重或变化检测。
  • 在排查中确认两段 payload 是否完全一致。

它不适合证明“是谁发的”。例如 webhook body 是:

{"event":"paid","amount":100}

发送方同时给了 sha256(body)。如果没有 secret,任何中间人都可以把金额改成 1,再重新计算 SHA-256。接收方看到 hash 匹配,只能说明 body 和 hash 是配套的,不能说明它来自可信发送方。

算法选择也要看用途。MD5 和 SHA-1 可以出现在旧 checksum 文档里,但不应作为新的安全边界。新做文件完整性或内容指纹时,SHA-256 通常是更稳妥的默认选择。对照第三方 checksum 时,不要自行升级算法;供应商给什么算法,就按它的算法和编码格式比较。

HMAC 适合 API 签名和 webhook 验证

HMAC 把消息、hash 函数和 secret key 组合起来。接收方用同一个 secret 对同一段消息重新计算;结果匹配,才说明发送方知道 secret,并且消息在签名后没有变化。

典型场景包括 webhook 签名、内部服务回调、开放 API 请求签名。比如平台发送:

X-Signature: sha256=5d4140...

你的服务端不能只看 header 是否存在,而要用平台分配的 webhook secret,对原始请求 body 或文档规定的 canonical string 重新计算 HMAC,再比较结果。

HMAC 不加密内容。消息仍然可以被看到;它验证的是真实性和完整性。如果你需要隐藏内容,还需要 TLS、加密存储或单独的加密层。

排查签名时,输入字节比算法名更容易出错

很多 HMAC mismatch 的根因不是 HMAC-SHA256 写错,而是被签名的输入不是同一段字节。常见差异包括:

  • 发送方签原始 body,接收方签解析后重新格式化的 JSON。
  • 一边包含 timestamp、method、path,另一边只签 body。
  • query 参数排序、大小写、URL 编码规则不同。
  • secret 是 Base64 编码后的字节,一端却把它当普通字符串。
  • 输出一边是 hex,一边是 Base64,或者 header 里带 sha256= 前缀。

JSON 尤其容易误导。下面两段语义相同,但字节不同:

{"a":1,"b":2}
{
  "b": 2,
  "a": 1
}

如果供应商签的是原始 body,你就不能拿格式化后的 JSON 去算 HMAC。

Checksum、签名和加密的边界

三者经常被混用:checksum 用于发现意外损坏;HMAC 用于认证消息;加密用于隐藏内容。一个文件 checksum 可以公开,因为它只是让下载者确认文件没变。一个 webhook signing secret 不能公开,因为它用于证明请求来源。一个密文能否解开,取决于加密算法、key、IV 和参数,而不是 hash 或 HMAC。

如果你看到一段摘要,不要问“能不能解码”。现代 hash 和 HMAC 的正常用法都是重新计算并比较,不是反解。

JWT:解码可读,不等于验证通过

JWT 也会让人混淆。用 JWT Decoder 可以查看 header 和 payload,但这只是 Base64URL 解码。真正接受一个 JWT 前,还要验证签名算法、key、issuer、audience、过期时间、not-before、权限范围等规则。

因此,“我能看到 payload 里的 userId”不代表 token 有效;“签名字段存在”也不代表它来自可信发行方。

选择标准

  • 只想知道两个文件或字符串是否相同:用 hash。
  • 想发布下载文件校验值:用 SHA-256 等 checksum。
  • 想验证 webhook/API 请求来自持有 secret 的发送方:用 HMAC。
  • 想隐藏消息内容:用加密,不是 HMAC。
  • 想查看 JWT claims:可以解码。
  • 想信任 JWT:必须在后端完整验证签名和 claims。

FAQ

可以把 SHA-256 当 API 签名吗?

如果没有 secret,不应该。普通 SHA-256 只能证明摘要和消息匹配,不能证明发送方身份。API 签名通常需要 HMAC 或非对称签名。

HMAC 的 secret 应该怎么保存?

应放在受控的服务端 secret 管理或环境配置中,不进入前端代码、公开仓库、截图和工单。排查时使用测试 secret 复现格式。

为什么平台给的签名和我算的不一样?

先检查原始输入字节、canonical string、secret 格式、算法、输出编码和 header 前缀。不要只盯着 HMAC 函数名。

继续阅读