-
This commit is contained in:
@ -2,8 +2,14 @@ package middleware
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/hmac"
|
||||
"crypto/sha256"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"io"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
@ -13,6 +19,7 @@ import (
|
||||
|
||||
"github.com/kms/api-key-service/internal/cache"
|
||||
"github.com/kms/api-key-service/internal/config"
|
||||
"github.com/kms/api-key-service/internal/repository"
|
||||
)
|
||||
|
||||
// SecurityMiddleware provides various security features
|
||||
@ -20,17 +27,19 @@ type SecurityMiddleware struct {
|
||||
config config.ConfigProvider
|
||||
logger *zap.Logger
|
||||
cacheManager *cache.CacheManager
|
||||
appRepo repository.ApplicationRepository
|
||||
rateLimiters map[string]*rate.Limiter
|
||||
mu sync.RWMutex
|
||||
}
|
||||
|
||||
// NewSecurityMiddleware creates a new security middleware
|
||||
func NewSecurityMiddleware(config config.ConfigProvider, logger *zap.Logger) *SecurityMiddleware {
|
||||
func NewSecurityMiddleware(config config.ConfigProvider, logger *zap.Logger, appRepo repository.ApplicationRepository) *SecurityMiddleware {
|
||||
cacheManager := cache.NewCacheManager(config, logger)
|
||||
return &SecurityMiddleware{
|
||||
config: config,
|
||||
logger: logger,
|
||||
cacheManager: cacheManager,
|
||||
appRepo: appRepo,
|
||||
rateLimiters: make(map[string]*rate.Limiter),
|
||||
}
|
||||
}
|
||||
@ -364,8 +373,46 @@ func (s *SecurityMiddleware) RequestSignatureMiddleware(next http.Handler) http.
|
||||
return
|
||||
}
|
||||
|
||||
// TODO: Implement actual signature validation
|
||||
// This would involve validating the HMAC signature using the client's secret
|
||||
// Implement HMAC signature validation
|
||||
appID := r.Header.Get("X-App-ID")
|
||||
if appID == "" {
|
||||
s.logger.Warn("Missing App-ID header for signature validation",
|
||||
zap.String("path", r.URL.Path),
|
||||
zap.String("client_ip", s.getClientIP(r)))
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
w.Write([]byte(`{"error":"missing_app_id","message":"X-App-ID header required for signature validation"}`))
|
||||
return
|
||||
}
|
||||
|
||||
// Retrieve application to get HMAC key
|
||||
ctx := r.Context()
|
||||
app, err := s.appRepo.GetByID(ctx, appID)
|
||||
if err != nil {
|
||||
s.logger.Warn("Failed to retrieve application for signature validation",
|
||||
zap.String("app_id", appID),
|
||||
zap.Error(err),
|
||||
zap.String("client_ip", s.getClientIP(r)))
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(`{"error":"invalid_application","message":"Invalid application ID"}`))
|
||||
return
|
||||
}
|
||||
|
||||
// Validate the signature
|
||||
if !s.validateHMACSignature(r, app.HMACKey, signature, timestamp) {
|
||||
s.logger.Warn("Invalid request signature",
|
||||
zap.String("app_id", appID),
|
||||
zap.String("path", r.URL.Path),
|
||||
zap.String("client_ip", s.getClientIP(r)))
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusUnauthorized)
|
||||
w.Write([]byte(`{"error":"invalid_signature","message":"Request signature is invalid"}`))
|
||||
return
|
||||
}
|
||||
|
||||
next.ServeHTTP(w, r)
|
||||
})
|
||||
@ -417,3 +464,33 @@ func (s *SecurityMiddleware) GetSecurityMetrics() map[string]interface{} {
|
||||
|
||||
return metrics
|
||||
}
|
||||
|
||||
// validateHMACSignature validates HMAC-SHA256 signature for request integrity
|
||||
func (s *SecurityMiddleware) validateHMACSignature(r *http.Request, hmacKey, signature, timestamp string) bool {
|
||||
// Create the signing string: METHOD + PATH + BODY + TIMESTAMP
|
||||
var bodyBytes []byte
|
||||
if r.Body != nil {
|
||||
var err error
|
||||
bodyBytes, err = io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
s.logger.Warn("Failed to read request body for signature validation", zap.Error(err))
|
||||
return false
|
||||
}
|
||||
// Restore the body for downstream handlers
|
||||
r.Body = io.NopCloser(strings.NewReader(string(bodyBytes)))
|
||||
}
|
||||
|
||||
signingString := fmt.Sprintf("%s\n%s\n%s\n%s",
|
||||
r.Method,
|
||||
r.URL.Path,
|
||||
string(bodyBytes),
|
||||
timestamp)
|
||||
|
||||
// Calculate expected signature
|
||||
mac := hmac.New(sha256.New, []byte(hmacKey))
|
||||
mac.Write([]byte(signingString))
|
||||
expectedSignature := hex.EncodeToString(mac.Sum(nil))
|
||||
|
||||
// Compare signatures (constant time comparison to prevent timing attacks)
|
||||
return hmac.Equal([]byte(signature), []byte(expectedSignature))
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user