security: trust x-real-ip over x-forwarded-for for rate-limit key#1
Open
advikdivekar wants to merge 1 commit into
Open
security: trust x-real-ip over x-forwarded-for for rate-limit key#1advikdivekar wants to merge 1 commit into
advikdivekar wants to merge 1 commit into
Conversation
Extract getClientIp into a shared lib/getClientIp.js that reads x-real-ip first (set by Vercel infrastructure, not spoofable) and falls back to walking x-forwarded-for right-to-left, skipping private ranges, so the rightmost public hop — not the client-controlled leftmost value — is used as the rate-limit key. Remove the duplicate inline getClientIp from contact/route.js and send-review/route.js and import the shared utility instead.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Both
app/api/contact/route.jsandapp/api/send-review/route.jsextracted the client IP by reading the leftmost value of theX-Forwarded-Forheader. That value is fully client-controlled — an attacker can rotate a fake IP on every request (X-Forwarded-For: 1.1.1.1,1.1.1.2, …) and the per-IP Redis sliding-window rate limiter never fires. Combined with a Turnstile-solving service, the Gmail account becomes an unlimited spam relay.What changed and why
lib/getClientIp.js(new)A single shared utility that resolves the verified client IP:
x-real-ipfirst — Vercel's edge sets this header and it cannot be forged by the client.x-forwarded-forright-to-left, skipping RFC-1918 / loopback ranges, returning the rightmost public hop (the last trusted-proxy-inserted value) rather than the leftmost client-supplied one.app/api/contact/route.jsRemoved the inline
getClientIpfunction (which read the spoofable leftmost XFF value) and imported the shared utility.app/api/send-review/route.jsSame removal and import as above.
Why this approach fixes the root cause
x-real-ipis injected by Vercel infrastructure after the request leaves the public internet — no client header can overwrite it. The right-to-left XFF walk is the industry-standard fallback: proxies append to the right, so the rightmost non-private IP is the last hop the infrastructure itself recorded, not a value the client supplied.Steps to test
npm run dev).POST /api/contactsix times with the headerX-Forwarded-For: 1.1.1.1— the 6th request must return429 Too Many Requests.X-Forwarded-For: 2.2.2.2on the 6th attempt — it must still be blocked because the rate-limit key is now derived fromx-real-ip(or the rightmost XFF hop set by the proxy), not from the spoofed leftmost value.POST /api/send-review.Edge cases covered
x-real-ippresent → used directly; XFF ignored.x-forwarded-forcontains only private IPs (e.g. internal load-balancer chain) → returns"unknown"; rate limiter key becomescontact:unknownand still enforces the limit.x-forwarded-forabsent andx-real-ipabsent → returns"unknown".x-forwarded-forvalues are filtered before the right-to-left walk.No regressions
npm run buildpasses without errors or warnings.Please review and merge this under GSSoC 2026.