package api import ( "context" "net" "net/http" "strings" "sync" "time" limiter "github.com/ulule/limiter/v3" memory "github.com/ulule/limiter/v3/drivers/store/memory" stdmiddleware "github.com/ulule/limiter/v3/drivers/middleware/stdlib" ) // SybilProtector mantém IPs bloqueados temporariamente var sybilBlocklist = struct { mu sync.RWMutex block map[string]time.Time }{block: make(map[string]time.Time)} // isBlocked verifica se um IP está na lista de bloqueio func isBlocked(ip string) bool { sybilBlocklist.mu.RLock() defer sybilBlocklist.mu.RUnlock() expire, exists := sybilBlocklist.block[ip] return exists && time.Now().Before(expire) } // blockIP adiciona um IP à blocklist por X segundos func blockIP(ip string, duration time.Duration) { sybilBlocklist.mu.Lock() defer sybilBlocklist.mu.Unlock() sybilBlocklist.block[ip] = time.Now().Add(duration) } // extractIP extrai IP puro do RemoteAddr func extractIP(addr string) string { ip, _, err := net.SplitHostPort(addr) if err != nil { return addr } return ip } // NewRateLimiterMiddleware cria middleware de rate limiting e proteção anti-Sybil func NewRateLimiterMiddleware() func(http.Handler) http.Handler { rate := limiter.Rate{ Period: 1 * time.Second, Limit: 5, } store := memory.NewStore() limiterInstance := limiter.New(store, rate) middleware := stdmiddleware.NewMiddleware(limiterInstance) return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // Ignorar WebSocket if strings.HasPrefix(r.URL.Path, "/ws") { next.ServeHTTP(w, r) return } ip := extractIP(r.RemoteAddr) if isBlocked(ip) { w.WriteHeader(http.StatusTooManyRequests) WriteError(w, http.StatusTooManyRequests, "IP bloqueado temporariamente por abuso") return } ctx := context.WithValue(r.Context(), "real-ip", ip) rec := &responseRecorder{ResponseWriter: w, status: 200} middleware.Handler(next).ServeHTTP(rec, r.WithContext(ctx)) if rec.status == http.StatusTooManyRequests { blockIP(ip, 30*time.Second) } }) } } // responseRecorder intercepta status code para detectar excesso de requisições type responseRecorder struct { http.ResponseWriter status int } func (rr *responseRecorder) WriteHeader(code int) { rr.status = code rr.ResponseWriter.WriteHeader(code) }