fix(security): add per-IP rate limiting to all authentication endpoints
Implement sliding window rate limiter to prevent brute-force attacks: - /auth and /login: 20 requests/minute per IP - /change: 10 POST requests/minute per IP - /lost: 5 POST requests/minute per IP (prevents email spam and user enumeration) - /reset: 10 POST requests/minute per IP - /api/v1/aliases: 30 requests/minute per IP Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
93673510d8
commit
2a9eec233a
6 changed files with 101 additions and 0 deletions
63
ratelimit.go
Normal file
63
ratelimit.go
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
type rateLimiter struct {
|
||||
mu sync.Mutex
|
||||
counts map[string][]time.Time
|
||||
limit int
|
||||
window time.Duration
|
||||
}
|
||||
|
||||
func newRateLimiter(limit int, window time.Duration) *rateLimiter {
|
||||
return &rateLimiter{
|
||||
counts: make(map[string][]time.Time),
|
||||
limit: limit,
|
||||
window: window,
|
||||
}
|
||||
}
|
||||
|
||||
func (rl *rateLimiter) Allow(key string) bool {
|
||||
rl.mu.Lock()
|
||||
defer rl.mu.Unlock()
|
||||
|
||||
now := time.Now()
|
||||
windowStart := now.Add(-rl.window)
|
||||
|
||||
timestamps := rl.counts[key]
|
||||
filtered := timestamps[:0]
|
||||
for _, t := range timestamps {
|
||||
if t.After(windowStart) {
|
||||
filtered = append(filtered, t)
|
||||
}
|
||||
}
|
||||
|
||||
if len(filtered) >= rl.limit {
|
||||
rl.counts[key] = filtered
|
||||
return false
|
||||
}
|
||||
|
||||
rl.counts[key] = append(filtered, now)
|
||||
return true
|
||||
}
|
||||
|
||||
var (
|
||||
authLimiter = newRateLimiter(20, time.Minute)
|
||||
changeLimiter = newRateLimiter(10, time.Minute)
|
||||
lostLimiter = newRateLimiter(5, time.Minute)
|
||||
resetLimiter = newRateLimiter(10, time.Minute)
|
||||
aliasLimiter = newRateLimiter(30, time.Minute)
|
||||
)
|
||||
|
||||
func remoteIP(r *http.Request) string {
|
||||
host, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
return r.RemoteAddr
|
||||
}
|
||||
return host
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue