f
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package crypto
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
@@ -10,6 +11,8 @@ import (
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"tyapi-server/internal/shared/interfaces"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -95,6 +98,57 @@ func VerifySignature(params map[string]string, secretKey string, timestamp int64
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifySignatureWithNonceCheck 验证HMAC-SHA256签名并检查nonce唯一性(防止重放攻击)
|
||||
// params: 请求参数map(包含signature字段)
|
||||
// secretKey: 签名密钥
|
||||
// timestamp: 时间戳(秒)
|
||||
// nonce: 随机字符串
|
||||
// cache: 缓存服务,用于存储已使用的nonce
|
||||
// cacheKeyPrefix: 缓存键前缀
|
||||
func VerifySignatureWithNonceCheck(
|
||||
ctx context.Context,
|
||||
params map[string]string,
|
||||
secretKey string,
|
||||
timestamp int64,
|
||||
nonce string,
|
||||
cache interfaces.CacheService,
|
||||
cacheKeyPrefix string,
|
||||
) error {
|
||||
// 1. 先进行基础签名验证
|
||||
if err := VerifySignature(params, secretKey, timestamp, nonce); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 2. 检查nonce是否已被使用(防止重放攻击)
|
||||
// 使用请求指纹:phone+timestamp+nonce 作为唯一标识
|
||||
phone := params["phone"]
|
||||
if phone == "" {
|
||||
return errors.New("手机号不能为空")
|
||||
}
|
||||
|
||||
// 构建nonce唯一性检查的缓存键
|
||||
nonceKey := fmt.Sprintf("%s:nonce:%s:%d:%s", cacheKeyPrefix, phone, timestamp, nonce)
|
||||
|
||||
// 检查nonce是否已被使用
|
||||
exists, err := cache.Exists(ctx, nonceKey)
|
||||
if err != nil {
|
||||
// 缓存查询失败,记录错误但继续验证(避免缓存故障导致服务不可用)
|
||||
return fmt.Errorf("检查nonce唯一性失败: %w", err)
|
||||
}
|
||||
if exists {
|
||||
return errors.New("请求已被使用,请勿重复提交")
|
||||
}
|
||||
|
||||
// 3. 将nonce标记为已使用,TTL设置为时间戳容差+1分钟(确保在容差范围内不会重复使用)
|
||||
ttl := time.Duration(SignatureTimestampTolerance+60) * time.Second
|
||||
if err := cache.Set(ctx, nonceKey, true, ttl); err != nil {
|
||||
// 记录错误但不影响验证流程(避免缓存故障导致服务不可用)
|
||||
return fmt.Errorf("标记nonce已使用失败: %w", err)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// 自定义编码字符集(不使用标准Base64字符集,增加破解难度)
|
||||
// 使用自定义字符集:数字+大写字母(排除易混淆的I和O)+小写字母(排除易混淆的i和l)+特殊字符
|
||||
const customEncodeCharset = "0123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghjkmnpqrstuvwxyz!@#$%^&*()_+-=[]{}|;:,.<>?"
|
||||
|
||||
Reference in New Issue
Block a user