diff --git a/internal/middleware/security_logging.go b/internal/middleware/security_logging.go deleted file mode 100644 index 446c81c..0000000 --- a/internal/middleware/security_logging.go +++ /dev/null @@ -1,268 +0,0 @@ -package middleware - -import ( - "log" - "net/http" - "net/url" - "os" - "strings" - "sync" - "time" -) - -type SecurityLogger struct { - logger *log.Logger -} - -func NewSecurityLogger() *SecurityLogger { - return &SecurityLogger{ - logger: log.New(os.Stdout, "[SECURITY] ", log.LstdFlags|log.Lshortfile), - } -} - -type SecurityEvent struct { - Type string - IP string - UserAgent string - Path string - Method string - UserID uint - Details string - Timestamp time.Time - Severity string -} - -func (sl *SecurityLogger) LogSecurityEvent(event SecurityEvent) { - sl.logger.Printf("[%s] %s - %s %s %s - UserID: %d - %s - %s", - event.Severity, - event.IP, - event.Method, - event.Path, - event.UserAgent, - event.UserID, - event.Type, - event.Details, - ) -} - -func SecurityLoggingMiddleware(logger *SecurityLogger) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - start := time.Now() - - rw := &securityResponseWriter{ResponseWriter: w, statusCode: http.StatusOK} - - next.ServeHTTP(rw, r) - - userUID := uint(0) - if u := GetUserIDFromContext(r.Context()); u != nil { - userUID = *u - } - ip := getClientIP(r) - - event := SecurityEvent{ - IP: ip, - UserAgent: r.UserAgent(), - Path: r.URL.Path, - Method: r.Method, - UserID: userUID, - Timestamp: start, - } - - switch { - case rw.statusCode >= 400 && rw.statusCode < 500: - event.Type = "Client Error" - event.Severity = "WARN" - event.Details = "Client error response" - case rw.statusCode >= 500: - event.Type = "Server Error" - event.Severity = "ERROR" - event.Details = "Server error response" - case strings.HasPrefix(r.URL.Path, "/api/auth/"): - event.Type = "Authentication" - event.Severity = "INFO" - event.Details = "Authentication endpoint accessed" - case strings.HasPrefix(r.URL.Path, "/api/posts/") && r.Method == "POST": - event.Type = "Post Creation" - event.Severity = "INFO" - event.Details = "Post creation attempt" - case strings.HasPrefix(r.URL.Path, "/api/posts/") && (r.Method == "PUT" || r.Method == "DELETE"): - event.Type = "Post Modification" - event.Severity = "INFO" - event.Details = "Post modification attempt" - default: - event.Type = "API Access" - event.Severity = "INFO" - event.Details = "API endpoint accessed" - } - - logger.LogSecurityEvent(event) - }) - } -} - -func SuspiciousActivityMiddleware(logger *SecurityLogger) func(http.Handler) http.Handler { - return func(next http.Handler) http.Handler { - return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - ip := getClientIP(r) - userAgent := r.UserAgent() - - suspicious := false - details := "" - - pathProbe := layeredUnescape(r.URL.Path, url.PathUnescape) - queryProbe := layeredUnescape(r.URL.RawQuery, url.QueryUnescape) - - if containsSQLInjection(pathProbe) || containsSQLInjection(queryProbe) { - suspicious = true - details = "Potential SQL injection attempt" - } - - if containsXSS(pathProbe) || containsXSS(queryProbe) { - suspicious = true - details = "Potential XSS attempt" - } - - if isSuspiciousUserAgent(userAgent) { - suspicious = true - details = "Suspicious user agent" - } - - if isRapidRequest(ip) { - suspicious = true - details = "Rapid request pattern" - } - - if suspicious { - event := SecurityEvent{ - Type: "Suspicious Activity", - IP: ip, - UserAgent: userAgent, - Path: r.URL.Path, - Method: r.Method, - Details: details, - Timestamp: time.Now(), - Severity: "WARN", - } - logger.LogSecurityEvent(event) - } - - next.ServeHTTP(w, r) - }) - } -} - -type securityResponseWriter struct { - http.ResponseWriter - statusCode int -} - -func (rw *securityResponseWriter) WriteHeader(code int) { - rw.statusCode = code - rw.ResponseWriter.WriteHeader(code) -} - -func getClientIP(r *http.Request) string { - return GetSecureClientIP(r) -} - -func layeredUnescape(s string, decoder func(string) (string, error)) string { - out := s - for range 3 { - d, err := decoder(out) - if err != nil || d == out { - return out - } - out = d - } - return out -} - -func containsSQLInjection(input string) bool { - sqlPatterns := []string{ - "' OR '1'='1", - "'; DROP TABLE", - "UNION SELECT", - "INSERT INTO", - "DELETE FROM", - "UPDATE SET", - } - - input = strings.ToUpper(input) - for _, pattern := range sqlPatterns { - if strings.Contains(input, strings.ToUpper(pattern)) { - return true - } - } - return false -} - -func containsXSS(input string) bool { - xssPatterns := []string{ - "", true}, - {"javascript:alert('xss')", true}, - {"onload=alert('xss')", true}, - {"onerror=alert('xss')", true}, - {"onclick=alert('xss')", true}, - {"