zero-ppanel/pkg/signature/validator.go

65 lines
1.5 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 signature
import (
"context"
"crypto/hmac"
"crypto/sha256"
"encoding/hex"
"strconv"
"time"
)
// Validator 验证签名
type Validator struct {
conf SignatureConf
nonceStore NonceStore
}
func NewValidator(conf SignatureConf, store NonceStore) *Validator {
return &Validator{conf: conf, nonceStore: store}
}
func (v *Validator) windowSeconds() int64 {
if v.conf.ValidWindowSeconds <= 0 {
return 300
}
return v.conf.ValidWindowSeconds
}
// Validate 验证请求签名,返回具体错误便于调用方映射到 xerr 错误码
func (v *Validator) Validate(ctx context.Context, appId, timestamp, nonce, signature, stringToSign string) error {
// 1. appId 是否存在
secret, ok := v.conf.AppSecrets[appId]
if !ok {
return ErrSignatureMissing
}
// 2. 时间窗口
ts, err := strconv.ParseInt(timestamp, 10, 64)
if err != nil {
return ErrSignatureExpired
}
diff := time.Now().Unix() - ts
if diff < 0 {
diff = -diff
}
if diff > v.windowSeconds() {
return ErrSignatureExpired
}
// 3. Nonce 防重放Redis 不可用时降级,仅时间窗口)
replayed, err := v.nonceStore.SetIfNotExists(ctx, appId, nonce, v.windowSeconds()+60)
if err == nil && replayed {
return ErrSignatureReplay
}
// 4. HMAC 验签(防时序攻击)
mac := hmac.New(sha256.New, []byte(secret))
mac.Write([]byte(stringToSign))
expected := hex.EncodeToString(mac.Sum(nil))
if !hmac.Equal([]byte(expected), []byte(signature)) {
return ErrSignatureInvalid
}
return nil
}