Skip to content

[MEDIUM] fix(security): prevent rate-limit bypass via X-Forwarded-For header spoofing#79

Open
katnisscalls99 wants to merge 1 commit into
profullstack:masterfrom
katnisscalls99:fix/rate-limiter-ip-spoofing
Open

[MEDIUM] fix(security): prevent rate-limit bypass via X-Forwarded-For header spoofing#79
katnisscalls99 wants to merge 1 commit into
profullstack:masterfrom
katnisscalls99:fix/rate-limiter-ip-spoofing

Conversation

@katnisscalls99
Copy link
Copy Markdown

VULNERABILITY: Both getClientIp() implementations (rate-limiter.js and middleware.js) blindly trusted the first value in the X-Forwarded-For header. Because HTTP clients fully control this header when there is no upstream proxy stripping it, an attacker can rotate it on every request to bypass all IP-based rate limiting.

Affected endpoints (all rely on IP-based limiting):

  • POST /api/auth/send-sms — 5 req/min (OTP spam, SMS cost amplification)
  • POST /api/auth/verify-sms — 5 req/min (OTP brute-force)
  • POST /api/auth/backup-pin — 10 req/min via middleware (PIN brute-force)
  • All /api/* endpoints — 60 req/min via middleware

Attack PoC:

for i in {1..1000}; do
  curl -X POST /api/auth/send-sms \
    -H "X-Forwarded-For: 1.2.3.$i" \
    -d '{"phoneNumber":"+15551234567"}'
done
# Each request sees a different IP → rate limit never triggers.

Fix:

  • Introduce TRUSTED_PROXY_COUNT env var (default 0).
  • When > 0, read the correct IP from the right-hand side of X-Forwarded-For (the proxy-appended, unforged position).
  • When 0 (direct internet, no trusted proxy), skip X-Forwarded-For entirely and rely only on X-Real-IP, which nginx/ALB typically sets from the real connection address after stripping any client-supplied value.

Operators behind Cloudflare / nginx should set TRUSTED_PROXY_COUNT=1 in their deployment environment.

Severity: MEDIUM — bypasses brute-force / spam protections on auth endpoints

…poofing

VULNERABILITY: Both getClientIp() implementations (rate-limiter.js and
middleware.js) blindly trusted the first value in the X-Forwarded-For header.
Because HTTP clients fully control this header when there is no upstream proxy
stripping it, an attacker can rotate it on every request to bypass all
IP-based rate limiting.

Affected endpoints (all rely on IP-based limiting):
  - POST /api/auth/send-sms   — 5 req/min (OTP spam, SMS cost amplification)
  - POST /api/auth/verify-sms — 5 req/min (OTP brute-force)
  - POST /api/auth/backup-pin — 10 req/min via middleware (PIN brute-force)
  - All /api/* endpoints      — 60 req/min via middleware

Attack PoC:
  for i in {1..1000}; do
    curl -X POST /api/auth/send-sms \
      -H "X-Forwarded-For: 1.2.3.$i" \
      -d '{"phoneNumber":"+15551234567"}'
  done
  # Each request sees a different IP → rate limit never triggers.

Fix:
- Introduce TRUSTED_PROXY_COUNT env var (default 0).
- When > 0, read the correct IP from the right-hand side of X-Forwarded-For
  (the proxy-appended, unforged position).
- When 0 (direct internet, no trusted proxy), skip X-Forwarded-For entirely
  and rely only on X-Real-IP, which nginx/ALB typically sets from the real
  connection address after stripping any client-supplied value.
- Add detailed inline documentation to guide operators.

Operators behind Cloudflare / nginx should set TRUSTED_PROXY_COUNT=1 in their
deployment environment.

Severity: MEDIUM — bypasses brute-force / spam protections on auth endpoints
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