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

    Fields

    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