diff options
| author | bndw <ben@bdw.to> | 2026-02-15 17:08:21 -0800 |
|---|---|---|
| committer | bndw <ben@bdw.to> | 2026-02-15 17:08:21 -0800 |
| commit | fa8b4bb939430ba9c48fb4d3ba2b0e9095e229d3 (patch) | |
| tree | 3935f17ed94a95bef7f5c68a4b98e0f6fab6b83d /internal/handler/websocket/handler.go | |
| parent | 84dc5995f28e6d63d3cbb1af44daba7f10cbca66 (diff) | |
fix: move rate limiting before auth checks
- Rate limiting now happens immediately after parsing
- Prevents spam from wasting resources on auth validation
- Protects against DoS from unauthenticated request floods
- IP-based rate limits now apply to all spam, not just authenticated users
Diffstat (limited to 'internal/handler/websocket/handler.go')
| -rw-r--r-- | internal/handler/websocket/handler.go | 46 |
1 files changed, 24 insertions, 22 deletions
diff --git a/internal/handler/websocket/handler.go b/internal/handler/websocket/handler.go index 909e2ec..c1b23b8 100644 --- a/internal/handler/websocket/handler.go +++ b/internal/handler/websocket/handler.go | |||
| @@ -265,19 +265,8 @@ func (h *Handler) handleEvent(ctx context.Context, conn *websocket.Conn, raw []j | |||
| 265 | return fmt.Errorf("invalid event: %w", err) | 265 | return fmt.Errorf("invalid event: %w", err) |
| 266 | } | 266 | } |
| 267 | 267 | ||
| 268 | if err := h.requireAuth(ctx, conn, true, state); err != nil { | ||
| 269 | status = "unauthorized" | ||
| 270 | h.sendOK(ctx, conn, event.ID, false, err.Error()) | ||
| 271 | return nil | ||
| 272 | } | ||
| 273 | |||
| 274 | if state.authenticatedPubkey == "" && h.authConfig != nil && h.authConfig.WriteEnabled { | ||
| 275 | status = "unauthenticated" | ||
| 276 | h.sendOK(ctx, conn, event.ID, false, "auth-required: authentication required") | ||
| 277 | return nil | ||
| 278 | } | ||
| 279 | |||
| 280 | // Rate limiting - use pubkey if authenticated, otherwise IP | 268 | // Rate limiting - use pubkey if authenticated, otherwise IP |
| 269 | // Check this BEFORE auth to prevent spam from wasting resources | ||
| 281 | if h.limiter != nil { | 270 | if h.limiter != nil { |
| 282 | identifier := state.authenticatedPubkey | 271 | identifier := state.authenticatedPubkey |
| 283 | if identifier == "" { | 272 | if identifier == "" { |
| @@ -290,6 +279,18 @@ func (h *Handler) handleEvent(ctx context.Context, conn *websocket.Conn, raw []j | |||
| 290 | } | 279 | } |
| 291 | } | 280 | } |
| 292 | 281 | ||
| 282 | if err := h.requireAuth(ctx, conn, true, state); err != nil { | ||
| 283 | status = "unauthorized" | ||
| 284 | h.sendOK(ctx, conn, event.ID, false, err.Error()) | ||
| 285 | return nil | ||
| 286 | } | ||
| 287 | |||
| 288 | if state.authenticatedPubkey == "" && h.authConfig != nil && h.authConfig.WriteEnabled { | ||
| 289 | status = "unauthenticated" | ||
| 290 | h.sendOK(ctx, conn, event.ID, false, "auth-required: authentication required") | ||
| 291 | return nil | ||
| 292 | } | ||
| 293 | |||
| 293 | // Event already parsed above for auth check | 294 | // Event already parsed above for auth check |
| 294 | 295 | ||
| 295 | if !event.CheckID() { | 296 | if !event.CheckID() { |
| @@ -376,17 +377,8 @@ func (h *Handler) handleReq(ctx context.Context, conn *websocket.Conn, raw []jso | |||
| 376 | return fmt.Errorf("REQ expects at least 3 elements") | 377 | return fmt.Errorf("REQ expects at least 3 elements") |
| 377 | } | 378 | } |
| 378 | 379 | ||
| 379 | if err := h.requireAuth(ctx, conn, false, state); err != nil { | ||
| 380 | status = "unauthorized" | ||
| 381 | return err | ||
| 382 | } | ||
| 383 | |||
| 384 | if state.authenticatedPubkey == "" && h.authConfig != nil && h.authConfig.ReadEnabled { | ||
| 385 | status = "unauthenticated" | ||
| 386 | return nil | ||
| 387 | } | ||
| 388 | |||
| 389 | // Rate limiting - use pubkey if authenticated, otherwise IP | 380 | // Rate limiting - use pubkey if authenticated, otherwise IP |
| 381 | // Check this BEFORE auth to prevent spam from wasting resources | ||
| 390 | if h.limiter != nil { | 382 | if h.limiter != nil { |
| 391 | identifier := state.authenticatedPubkey | 383 | identifier := state.authenticatedPubkey |
| 392 | if identifier == "" { | 384 | if identifier == "" { |
| @@ -398,6 +390,16 @@ func (h *Handler) handleReq(ctx context.Context, conn *websocket.Conn, raw []jso | |||
| 398 | } | 390 | } |
| 399 | } | 391 | } |
| 400 | 392 | ||
| 393 | if err := h.requireAuth(ctx, conn, false, state); err != nil { | ||
| 394 | status = "unauthorized" | ||
| 395 | return err | ||
| 396 | } | ||
| 397 | |||
| 398 | if state.authenticatedPubkey == "" && h.authConfig != nil && h.authConfig.ReadEnabled { | ||
| 399 | status = "unauthenticated" | ||
| 400 | return nil | ||
| 401 | } | ||
| 402 | |||
| 401 | var subID string | 403 | var subID string |
| 402 | if err := json.Unmarshal(raw[1], &subID); err != nil { | 404 | if err := json.Unmarshal(raw[1], &subID); err != nil { |
| 403 | status = "error" | 405 | status = "error" |
