Skip to content

Rate limit Redis keys are not scoped to the configured route #10624

Description

@SebC99

New Issue Checklist

  • This is not a security vulnerability; it is a rate-limit isolation bug.
  • I searched the existing issues but did not find this behavior reported.

Issue Description

When configuring more than one rateLimit rule with redisUrl, rate-limit state is not scoped to the configured route, nor to the individual rule/window.

Steps to reproduce

Configure two rules with the same Redis instance:

rateLimit: [
  {
    requestPath: '/functions/a',
    requestMethods: ['POST'],
    requestTimeWindow: 30 * 24 * 60 * 60 * 1000,
    requestCount: 10,
    zone: 'user',
    redisUrl: 'redis://localhost:6379'
  },
  {
    requestPath: '/functions/b',
    requestMethods: ['POST'],
    requestTimeWindow: 60 * 60 * 1000,
    requestCount: 5,
    zone: 'user',
    redisUrl: 'redis://localhost:6379'
  }
]

For the same authenticated user:

  1. Call POST /functions/a. This creates Redis key rl:<userId> with a 30-day TTL.
  2. Call POST /functions/b. It increments that same key despite using a different path and rate-limit rule.
  3. The second rule applies its max: 5 to the shared hit count, while retaining the 30-day TTL.

Reverse the order and the shared key expires after one hour instead.

Actual Outcome

requestPath is used only to select the middleware. The key generator returns only the app ID, session token, user ID, or IP address.

With Redis, each RedisStore is constructed without a prefix, so rate-limit-redis uses its default rl: prefix for every configured rule. Different endpoints therefore share rl:<userId>.

This also means that rules with different windowMs values share a TTL. rate-limit-redis sets the TTL only when the key is first created (unless expiry reset is enabled), so the effective window depends on which endpoint is called first. Multiple limits for the same endpoint cannot be composed safely either.

Expected Outcome

Each independent rate-limit rule should use its own Redis key namespace. At a minimum, Redis keys should be scoped by requestPath, method, and zone. To safely support multiple limits on the same endpoint, the namespace should also distinguish the configured window and count (or Parse Server should expose a rule identifier).

One possible fix would be to pass a deterministic prefix to RedisStore based on the rule identity, for example a compact hash of:

{ requestPath, requestMethods, zone, requestTimeWindow, requestCount }

followed by the existing user/session/IP key.

Environment

  • Parse Server version: 9.9.0 (HULAB fork based on Parse Server 9.9.0)
  • Operating system: Amazon Linux 2023
  • Local or remote host: AWS Elastic Beanstalk
  • Database: MongoDB (not involved)
  • Client SDK: not involved; reproducible with HTTP requests

Logs

No server error is emitted. The collision is visible directly in Redis as the same rl:<userId> key being incremented by both routes.

Metadata

Metadata

Assignees

No one assigned

    Labels

    type:bugImpaired feature or lacking behavior that is likely assumed

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions