hmac.new需传sha256.new(构造函数)而非sha256.sum256(结果类型),因后者不满足func() hash.hash签名;密钥须32字节以上随机值,原文格式、编码、时间戳、body处理须前后端严格一致。

为什么 hmac.New 要传 sha256.New 而不是 sha256.Sum256
因为 hmac.New 第二个参数要求是哈希函数的构造器(func() hash.Hash),不是哈希结果类型。传 sha256.Sum256 会编译报错:cannot use sha256.Sum256 (type sha256.Sum256) as type func() hash.Hash。
-
sha256.New是一个函数,每次调用返回新的hash.Hash实例,满足 HMAC 内部两次哈希的需要 -
sha256.Sum256是一个结构体类型,代表一次哈希计算的结果,不能用于初始化 HMAC 上下文 - 其他算法同理:要用
sha1.New、md5.New,不能用sha1.Sum或md5.Sum
签名原文不标准化,generateHMAC 每次算出的值都对不上
常见错误是客户端拼接字符串随意加空格、换行、字段顺序不固定,或漏掉 timestamp、nonce 等关键字段;服务端解析时又按不同逻辑重组,导致 HMAC 输入不一致。
- 必须约定严格格式,例如:
method|path|timestamp|nonce|body_hash(全部小写,竖线分隔,无空格) - GET 请求的
body_hash固定为空字符串"";POST/PUT 必须先完整读取c.Request.Body,再用sha256.Sum256()计算,并还原 Body 流(否则后续c.ShouldBindJSON会失败) - 时间戳统一用秒级 Unix 时间(
time.Now().Unix()),且客户端应先调用/api/servertime校准本地时钟
hmac.Equal 报错 undefined?你可能没导入 crypto/subtle
hmac.Equal 不在 crypto/hmac 包里,它属于 crypto/subtle——这是专门提供恒定时间比较函数的安全包,防止时序攻击。
- 直接用
==比较两个[]byte签名会暴露字节差异的时间特征,容易被侧信道攻击利用 - 正确做法:
import "crypto/subtle",然后用subtle.ConstantTimeCompare([]byte(sig1), []byte(sig2)) == 1 - 注意:前后端编码方式必须一致——如果客户端用
base64.StdEncoding.EncodeToString,服务端解码也必须用base64.StdEncoding.DecodeString,不能混用RawStdEncoding(会丢=导致解码失败)
密钥硬编码 + 长度随意 = 白给攻击面
把 secret := "my-key-123" 写死在代码里,等于把 API 防线撕开一道口子;而密钥太短(如 8 字节)或含可预测模式(如时间戳+用户名),会让 HMAC-SHA256 实际安全性远低于理论强度。
立即学习“go语言免费学习笔记(深入)”;
- 生产环境密钥应为 32 字节以上高强度随机序列(可用
openssl rand -hex 32生成),并通过环境变量或 KMS 注入,绝不硬编码 -
hmac.New对密钥长度敏感:SHA-256 推荐密钥 ≥ 32 字节;若密钥过长,底层会先哈希截断,但弱密钥无法靠哈希补救 - 别拼接字符串当密钥,比如
env + username + salt——这种“自研密钥派生”既不标准也不安全
hmac.New,而是让客户端和服务端对“同一段原文”达成比特级一致,且全程避开时序、编码、Body 读取、时钟漂移这些看不见的坑。










