← 中文资源

简体中文版本

恒定时间 HMAC 比较

计算 HMAC-SHA256 标签并以恒定时间比较,避免通过时序泄漏密钥信息。

阅读英文原文

功能说明

该示例用共享密钥对消息计算 HMAC-SHA256 标签,并以恒定时间函数比较提供的标签与期望值。恒定时间比较遍历全部字节而不过早返回,使攻击者无法通过响应耗时逐字节推断正确标签,这在验签接口上尤为重要,因为普通相等比较会因首个不同字节位置而提前退出。

关键代码

Sign 用 hmac.New 构建写消息并取摘要,密钥与消息均为字节切片。Verify 先算出期望标签,再用 subtle.ConstantTimeCompare 比较,返回一表示相等。二者长度不同时该函数立即返回零,因此长度不匹配也会失败而不会索引越界;调用方无需自行对齐长度。SignHex 提供十六进制字符串形式,便于放进以文本传输的头部。

使用注意

验签应在任何解析或副作用之前进行;计算所用消息必须与签名时的原始字节完全一致,框架若重新编码 JSON 会使校验失败。在该 API 之外自行用普通比较或提前返回会重新引入时序泄漏,务必坚持恒定时间比较。密钥来自密钥管理服务,并支持轮换时同时接受新旧两把;长度比较本身可能轻微泄漏长度信息,但攻击者无法据此推出内容。

代码示例

package signing

import (
    "crypto/hmac"
    "crypto/sha256"
    "crypto/subtle"
    "encoding/hex"
    "errors"
)

var ErrInvalidMAC = errors.New("invalid message authentication code")

func Sign(secret, message []byte) []byte {
    mac := hmac.New(sha256.New, secret)
    mac.Write(message)
    return mac.Sum(nil)
}

func Verify(secret, message, provided []byte) error {
    expected := Sign(secret, message)
    if subtle.ConstantTimeCompare(expected, provided) != 1 {
        return ErrInvalidMAC
    }
    return nil
}

func SignHex(secret, message []byte) string {
    return hex.EncodeToString(Sign(secret, message))
}