security: Add rate limiting, input validation, and tenant isolation on all handlers
- Add rate limiting per endpoint (login: 5/min, API: 100/min) - Add input validation helpers (email, UUID, string, int) - Add tenant isolation to all handlers - Remove old validation.go, replace with input.go - Fix service/customer.go to use new validation functions - Build successful
This commit is contained in:
@@ -0,0 +1,132 @@
|
||||
package middleware
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var (
|
||||
// Email regex
|
||||
emailRegex = regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
|
||||
|
||||
// UUID regex
|
||||
uuidRegex = regexp.MustCompile(`^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$`)
|
||||
|
||||
// Safe string regex (tillåtna tecken)
|
||||
safeStringRegex = regexp.MustCompile(`^[a-zA-Z0-9\s\-_\.@,;:()\[\]{}]+$`)
|
||||
)
|
||||
|
||||
// ValidateEmail kontrollerar email-format
|
||||
func ValidateEmail(email string) error {
|
||||
if email == "" {
|
||||
return fmt.Errorf("email is required")
|
||||
}
|
||||
if len(email) > 254 {
|
||||
return fmt.Errorf("email too long")
|
||||
}
|
||||
if !emailRegex.MatchString(email) {
|
||||
return fmt.Errorf("invalid email format")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateUUID kontrollerar UUID-format
|
||||
func ValidateUUID(id string) error {
|
||||
if id == "" {
|
||||
return fmt.Errorf("id is required")
|
||||
}
|
||||
if !uuidRegex.MatchString(id) {
|
||||
return fmt.Errorf("invalid UUID format")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateString kontrollerar sträng-input
|
||||
func ValidateString(s string, minLen, maxLen int, required bool) error {
|
||||
if required && strings.TrimSpace(s) == "" {
|
||||
return fmt.Errorf("field is required")
|
||||
}
|
||||
if s != "" {
|
||||
if len(s) < minLen {
|
||||
return fmt.Errorf("must be at least %d characters", minLen)
|
||||
}
|
||||
if len(s) > maxLen {
|
||||
return fmt.Errorf("must be at most %d characters", maxLen)
|
||||
}
|
||||
// Kontrollera farliga tecken
|
||||
if !safeStringRegex.MatchString(s) {
|
||||
return fmt.Errorf("contains invalid characters")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidateInt kontrollerar heltal
|
||||
func ValidateInt(val int, min, max int, required bool) error {
|
||||
if required && val == 0 {
|
||||
return fmt.Errorf("field is required")
|
||||
}
|
||||
if val != 0 {
|
||||
if val < min {
|
||||
return fmt.Errorf("must be at least %d", min)
|
||||
}
|
||||
if val > max {
|
||||
return fmt.Errorf("must be at most %d", max)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ValidatePagination kontrollerar pagination-parametrar
|
||||
func ValidatePagination(page, limit int) (int, int, error) {
|
||||
if page < 1 {
|
||||
page = 1
|
||||
}
|
||||
if limit < 1 {
|
||||
limit = 20
|
||||
}
|
||||
if limit > 100 {
|
||||
limit = 100
|
||||
}
|
||||
return page, limit, nil
|
||||
}
|
||||
|
||||
// SanitizeString tar bort farliga tecken
|
||||
func SanitizeString(s string) string {
|
||||
// Ta bort null bytes
|
||||
s = strings.ReplaceAll(s, "\x00", "")
|
||||
// Ta bort kontrolltecken
|
||||
var result strings.Builder
|
||||
for _, r := range s {
|
||||
if r >= 32 || r == '\t' || r == '\n' || r == '\r' {
|
||||
result.WriteRune(r)
|
||||
}
|
||||
}
|
||||
return strings.TrimSpace(result.String())
|
||||
}
|
||||
|
||||
// ParseInt parse en sträng till heltal med validering
|
||||
func ParseInt(s string, defaultVal int) int {
|
||||
if s == "" {
|
||||
return defaultVal
|
||||
}
|
||||
val, err := strconv.Atoi(s)
|
||||
if err != nil {
|
||||
return defaultVal
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
// ParseFloat parse en sträng till float med validering
|
||||
func ParseFloat(s string, defaultVal float64) float64 {
|
||||
if s == "" {
|
||||
return defaultVal
|
||||
}
|
||||
val, err := strconv.ParseFloat(s, 64)
|
||||
if err != nil {
|
||||
return defaultVal
|
||||
}
|
||||
return val
|
||||
}
|
||||
Reference in New Issue
Block a user