fix: address high-severity security issues from review

- Use subtle.ConstantTimeCompare for static credential checks to
  prevent timing side-channel attacks
- Cap failCounts (100k) and rememberedCreds (10k) maps with eviction
  to prevent memory exhaustion from botnet-scale scanning
- Sweep expired credentials on each auth attempt
- Add configurable max_connections (default 500) with semaphore to
  limit concurrent connections and prevent goroutine/fd exhaustion

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-14 16:41:23 +01:00
parent 51fdea0c2f
commit a40110f2f5
6 changed files with 90 additions and 7 deletions

View File

@@ -1,6 +1,7 @@
package auth
import (
"fmt"
"sync"
"testing"
"time"
@@ -127,6 +128,31 @@ func TestCounterResetsAfterAcceptance(t *testing.T) {
}
}
func TestExpiredCredentialsSweep(t *testing.T) {
a := newTestAuth(2, time.Hour)
now := time.Now()
a.now = func() time.Time { return now }
// Create several remembered credentials by reaching the threshold.
for i := range 5 {
ip := fmt.Sprintf("10.0.0.%d", i)
a.Authenticate(ip, fmt.Sprintf("user%d", i), "pass")
a.Authenticate(ip, fmt.Sprintf("user%d", i), "pass")
}
if len(a.rememberedCreds) != 5 {
t.Fatalf("expected 5 remembered creds, got %d", len(a.rememberedCreds))
}
// Advance past TTL so all are expired, then trigger sweep.
a.now = func() time.Time { return now.Add(2 * time.Hour) }
a.Authenticate("99.99.99.99", "trigger", "sweep")
if len(a.rememberedCreds) != 0 {
t.Errorf("expected 0 remembered creds after sweep, got %d", len(a.rememberedCreds))
}
}
func TestConcurrentAccess(t *testing.T) {
a := newTestAuth(5, time.Hour)
var wg sync.WaitGroup