Skip to content

fix: prevent X-Forwarded-For rate-limit bypass - #1456

Open
ionfwsrijan wants to merge 1 commit into
Canopus-Labs:mainfrom
ionfwsrijan:fix/1438-xff-rate-limit-bypass
Open

fix: prevent X-Forwarded-For rate-limit bypass#1456
ionfwsrijan wants to merge 1 commit into
Canopus-Labs:mainfrom
ionfwsrijan:fix/1438-xff-rate-limit-bypass

Conversation

@ionfwsrijan

@ionfwsrijan ionfwsrijan commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Problem

app.set("trust proxy", 1) combined with the default keyGenerator (based on req.ip) lets any client send a spoofed X-Forwarded-For header. Because the server blindly trusts the first entry of that header, an attacker can rotate their perceived IP on every request and defeat every rate limiter (login brute-force protection, AI cost guards, general endpoint limits).

Fix

  • Replaced the blanket trust proxy: 1 in server.js with a REVERSE_PROXY_CIDR-based trust setting. X-Forwarded-For is now honored only when the request comes from a configured trusted proxy CIDR; when unset, req.ip is the direct socket address and the header is ignored entirely.
  • All limiters in rateLimiter.js now use an explicit IP keyGenerator that normalizes IPv6 via the express-rate-limit helper, so IPv6 callers cannot dodge limits by rotating addresses.
  • Documented the new REVERSE_PROXY_CIDR variable in .env.example.

Files changed

  • backend/server.js — conditional trust proxy from REVERSE_PROXY_CIDR.
  • backend/middlewares/rateLimiter.js — explicit ipKeyGenerator on all five limiters.
  • backend/.env.example — documented REVERSE_PROXY_CIDR.
  • backend/tests/rateLimiter.xff.bypass.unit.test.js — new test proving a rotated X-Forwarded-For no longer resets the limit bucket.

Testing

npx vitest run tests/rateLimiter.xff.bypass.unit.test.js — 1/1 passing (11th login attempt rejected with 429 despite spoofing a fresh IP each request).

Closes #1438

Summary

  • Replaced trust proxy: 1 with configurable REVERSE_PROXY_CIDR trust settings.
  • Added IPv6-normalized IP keys to all five rate limiters.
  • Added documentation and a regression test for rotating X-Forwarded-For values.
  • The test confirms that the 11th login attempt returns HTTP 429.

Ready to merge.

…figured proxy CIDR

Replace the blanket 'trust proxy: 1' with a REVERSE_PROXY_CIDR-based trust
setting so a client-supplied X-Forwarded-For header can no longer rotate the
rate-limit bucket. All limiters now use an explicit IP key generator that
normalizes IPv6 via the express-rate-limit helper, keeping brute-force and
AI cost guards effective.

Closes Canopus-Labs#1438
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e56fca04-c72f-4a38-b3da-7b130a195b44

📥 Commits

Reviewing files that changed from the base of the PR and between 8acb5b8 and e59bf1b.

📒 Files selected for processing (4)
  • backend/.env.example
  • backend/middlewares/rateLimiter.js
  • backend/server.js
  • backend/tests/rateLimiter.xff.bypass.unit.test.js

📝 Walkthrough

Walkthrough

The server now trusts forwarded client addresses only from configured proxy CIDRs. All rate limiters use a shared stable IP key generator, with IPv6 masking. A login integration test verifies that rotating X-Forwarded-For values cannot bypass the limit.

Changes

Proxy trust and rate limiting

Layer / File(s) Summary
Configure trusted proxy CIDRs
backend/.env.example, backend/server.js
The server parses REVERSE_PROXY_CIDR and enables Express proxy trust only when nonempty CIDRs are configured.
Apply stable IP keys and validate bypass behavior
backend/middlewares/rateLimiter.js, backend/tests/rateLimiter.xff.bypass.unit.test.js
All five rate limiters use the shared IP key generator. The test confirms that rotating X-Forwarded-For values still trigger HTTP 429 on the 11th login request.

Estimated code review effort: 2 (Simple) | ~10 minutes

Suggested reviewers: karanunique

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly describes the primary security fix: preventing rate-limit bypass through spoofed X-Forwarded-For headers.
Linked Issues check ✅ Passed The changes address issue #1438 by restricting proxy trust, using stable IP keys for all five limiters, documenting configuration, and adding a bypass regression test.
Out of Scope Changes check ✅ Passed The configuration, proxy-trust logic, limiter key generation, and regression test directly support issue #1438 and contain no unrelated changes.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

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.

Rate-limit bypass via spoofable X-Forwarded-For with trust proxy: 1

1 participant