Files
toom1996 10d8a96e8c update
2026-09-07 00:04:01 +08:00

61 lines
2.3 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// Package hmac 提供爬虫 → 后台 ingest 接口的 HMAC-SHA256 请求签名与校验。
//
// 设计目标(与公开接口「前端 JS 签名」区分):
// - 这里面向「服务端到服务端」的爬虫上报,密钥绝不下发到任何前端 bundle,
// 只存在于爬虫配置与后台 INGEST_SECRET 环境变量,安全性高得多。
// - 通过 X-Signature / X-Timestamp / X-Nonce 三头防篡改 + 防重放:
// - X-Timestamp 容忍窗口(默认 ±5 分钟)挡掉过期请求;
// - X-Nonce 一次性随机串,由后台入库去重挡掉重放(见 repository.IngestRepository 的 nonce 表)。
//
// 签名串拼接:HMAC_SHA256(secret, timestamp + "." + nonce + "." + bodyRaw)
// bodyRaw 是请求体的原始字节(未编码),保证签名与服务端收到的字节严格一致。
package hmac
import (
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strconv"
"strings"
"time"
)
// DefaultTTL 签名时间戳默认容忍窗口(秒)。
const DefaultTTL = 300
// Sign 生成签名:对 timestamp.nonce.body 做 HMAC-SHA256,返回十六进制串。
func Sign(secret, timestamp, nonce, body string) string {
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(timestamp))
mac.Write([]byte("."))
mac.Write([]byte(nonce))
mac.Write([]byte("."))
mac.Write([]byte(body))
return hex.EncodeToString(mac.Sum(nil))
}
// Verify 校验请求签名,同时检查时间戳窗口。
//
// 返回 (ok, error):ok=false 表示签名或时间戳不通过;error 仅用于内部异常(理论上不会返回)。
// 注:nonce 防重放不在此处判断,交由调用方(中间件 / 仓储)查库,
// 因为 nonce 是否重复依赖持久化状态,且失败时应返回 409 而非 401。
func Verify(secret, body, sigHeader, tsHeader, nonceHeader string, ttl int) bool {
if sigHeader == "" || tsHeader == "" || nonceHeader == "" {
return false
}
if ttl <= 0 {
ttl = DefaultTTL
}
ts, err := strconv.ParseInt(tsHeader, 10, 64)
if err != nil {
return false
}
now := time.Now().Unix()
if diff := now - ts; diff > int64(ttl) || diff < -int64(ttl) {
return false
}
expected := Sign(secret, tsHeader, nonceHeader, body)
// 定长比较防时序侧信道
return hmac.Equal([]byte(expected), []byte(strings.TrimSpace(sigHeader)))
}