Skip to content

fix(security): fall back to x-real-ip when TRUST_PROXY_COUNT is unset - #308

Merged
ety001 merged 1 commit into
nextfrom
fix/rate-limit-bucket-collapse
Jul 29, 2026
Merged

fix(security): fall back to x-real-ip when TRUST_PROXY_COUNT is unset#308
ety001 merged 1 commit into
nextfrom
fix/rate-limit-bucket-collapse

Conversation

@ety001

@ety001 ety001 commented Jul 29, 2026

Copy link
Copy Markdown
Member

Summary

Fixes audit V2 finding #3 (High): when TRUST_PROXY_COUNT is not configured, getClientIP() returned 'unknown' for every request, collapsing all clients into a single rate-limit bucket. One abuser could exhaust the shared quota and DoS the endpoint for all legitimate users. The comment claimed it fell back to "socket peer" but Next.js route handlers do not expose a synchronous socket peer.

Changes

src/lib/middleware/rate-limit.ts — new three-tier IP resolution in getClientIP():

  1. TRUST_PROXY_COUNT set: Nth-from-right XFF entry (most spoof-resistant) — unchanged.
  2. TRUST_PROXY_COUNT unset: fall back to x-real-ip. The reverse proxy (OpenResty/ELB) sets this by overwriting client-supplied values, so it is far harder to spoof than the append-only X-Forwarded-For. This prevents bucket collapse when an operator forgets TRUST_PROXY_COUNT, as long as the reverse proxy sets x-real-ip (standard practice).
  3. Neither present: 'unknown' + a one-time production warning log (so operators notice).

tests/unit/rate-limit-proxy.test.ts:

  • New test: x-real-ip fallback gives different IPs separate buckets.
  • Split the old "unknown" test into with/without x-real-ip cases.

Test plan

  • pnpm type-check — clean
  • pnpm test — 471 passed

When TRUST_PROXY_COUNT is not configured, getClientIP() returned 'unknown'
for every request, collapsing all clients into a single rate-limit bucket.
One abuser could exhaust the shared quota and DoS the endpoint for all
legitimate users. The comment claimed it fell back to 'socket peer' but
Next.js route handlers do not expose a synchronous socket peer.

New three-tier IP resolution:
1. TRUST_PROXY_COUNT set: Nth-from-right XFF entry (most spoof-resistant).
2. TRUST_PROXY_COUNT unset: fall back to x-real-ip. The reverse proxy
   (OpenResty/ELB) sets this by OVERWRITING client values, so it is far
   harder to spoof than the append-only X-Forwarded-For.
3. Neither present: 'unknown' + a one-time production warning log.

This prevents bucket collapse when an operator forgets TRUST_PROXY_COUNT,
as long as the reverse proxy sets x-real-ip (standard practice).

Tests: add x-real-ip fallback test (different IPs get separate buckets);
split the old 'unknown' test into with/without x-real-ip cases.
@ety001
ety001 merged commit b896b2b into next Jul 29, 2026
2 of 3 checks passed
@ety001
ety001 deleted the fix/rate-limit-bucket-collapse branch July 29, 2026 09:45
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant