Skip to content

Rate-limit keyGenerator uses request.ip — spoofable in non-proxied deployments #356

Description

@melvincarvalho

Summary

Rate-limited IDP routes use `(request) => request.ip` as the keyGenerator. Because the Fastify server is configured with `trustProxy: true`, `request.ip` is derived from `X-Forwarded-For` when present. In any deployment where JSS is not behind a proxy (or is misconfigured behind one that doesn't strip client-supplied `X-Forwarded-For`), an attacker can rotate that header to bypass the per-IP limit.

Affected endpoints (today):

Anywhere else that uses the same `config.rateLimit.keyGenerator: (request) => request.ip` pattern.

Why this matters

Brute-force protection is the only thing standing between bcrypt and a credential-stuffing attacker. A spoofable key generator effectively neutralizes it.

Options

A. Trust the TCP-level address

```js
keyGenerator: (request) => request.socket.remoteAddress
```

  • Cannot be spoofed from a remote attacker
  • Breaks behind a real reverse proxy: every request appears to come from the proxy IP, so legit users share a single rate-limit bucket

B. Tighten `trustProxy` to known proxies

Configure `trustProxy` with a list of trusted IPs (e.g. `['127.0.0.1', '::1']`) instead of the boolean `true`. `request.ip` then only honors `X-Forwarded-For` from those addresses.

  • Correct under both deployment modes (proxied + non-proxied)
  • Requires the operator to configure proxy IPs explicitly

C. Document the deployment requirement

Add a deployment note: "If running JSS without a reverse proxy, set `trustProxy: false`."

  • Cheapest fix, but easy to miss

Recommendation

B + C combined. Default `trustProxy` to `'loopback'` (Fastify supports this string shorthand) so localhost proxies work out of the box but external `X-Forwarded-For` is ignored. Operators with non-loopback proxies set the list explicitly. Document in deployment notes.

Acceptance

  • Audit all `keyGenerator: (request) => request.ip` callsites
  • Decide on the trustProxy default (recommend `'loopback'` over `true`)
  • Update config schema + envMap to support a trust-proxy IP list
  • Tests: spoofed `X-Forwarded-For` does not bypass rate limit on default config
  • Document in deployment guide

Context

Spotted by Copilot on PR #355. Predates that PR — same pattern is on existing `POST` routes. Filing for a server-wide fix rather than touching it just on the new `PUT`.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions