Skip to content

[High][Security] Replace IP-only in-memory API rate limiting with distributed key-aware limits #306

Description

@richardcmckinney

Summary

The public API rate limiter uses a process-local Map, keys limits by client IP, trusts forwarded headers directly, and applies the import-mode rate boost before API-key authentication.

Evidence

  • apps/web/src/lib/server/domains/api/rate-limit.ts:1-10 documents the in-memory, proxy-header-trusting design.
  • apps/web/src/lib/server/domains/api/rate-limit.ts:17 stores limiter state in a local Map.
  • apps/web/src/lib/server/domains/api/rate-limit.ts:58-92 limits by IP and import mode.
  • apps/web/src/lib/server/domains/api/rate-limit.ts:102-121 trusts cf-connecting-ip, x-forwarded-for, and x-real-ip.
  • apps/web/src/lib/server/domains/api/auth.ts:96-99 applies the limiter before authenticating the API key.
  • apps/web/src/lib/server/domains/api/auth.ts:97-98 reads x-import-mode before auth.
  • apps/web/src/lib/server/domains/api/auth.ts:120-122 only later confirms import mode is allowed for admin keys.

Impact

Multi-replica deployments can bypass limits by spreading traffic across instances. Directly exposed deployments can spoof forwarded headers. NATed customers can be rate-limited together. Unauthenticated callers can request the import-mode limit bucket before auth by sending x-import-mode: true.

Recommended fix

Move API limiting to Redis or another shared store. Use separate buckets for pre-auth failures, verified API key ID, principal ID, and IP. Only apply import-mode limits after successful admin authentication. Trust forwarded headers only when the immediate peer is a configured trusted proxy.

Acceptance criteria

  • Rate limits are shared across app replicas.
  • Verified API-key requests are limited primarily by API key or principal, not only IP.
  • Invalid-auth attempts have a strict pre-auth bucket.
  • Import-mode rate boosts apply only after admin key validation.
  • Forwarded headers are ignored unless the request came through a trusted proxy.
  • Tests cover spoofed x-forwarded-for, x-import-mode before auth, and multiple API keys behind one IP.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions