fix(antispam): harden spam guard with signed form tokens and proxy-safe IP extraction
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 19s
Build & Deploy / 🧪 QA (push) Successful in 1m17s
Build & Deploy / 🏗️ Build (push) Successful in 2m33s
Build & Deploy / 🚀 Deploy (push) Successful in 25s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 51s
Build & Deploy / 🔔 Notify (push) Successful in 2s
All checks were successful
Build & Deploy / 🔍 Prepare (push) Successful in 19s
Build & Deploy / 🧪 QA (push) Successful in 1m17s
Build & Deploy / 🏗️ Build (push) Successful in 2m33s
Build & Deploy / 🚀 Deploy (push) Successful in 25s
Build & Deploy / 🧪 Post-Deploy Verification (push) Successful in 51s
Build & Deploy / 🔔 Notify (push) Successful in 2s
- Rate limit now uses the last X-Forwarded-For hop (appended by Traefik) instead of the client-controlled first hop, which bots rotated freely - Time-trap anchored to server time via HMAC-signed form token (FORM_TOKEN_SECRET) instead of client-supplied form_loaded_at - Fix RequestQuoteForm regression: FormData was built from scratch without honeypot/token hidden inputs and inputs lacked name attributes, so every legitimate quote request was silently blocked as spam
This commit is contained in:
18
lib/antispam/client-ip.ts
Normal file
18
lib/antispam/client-ip.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
/**
|
||||
* Extracts the client IP from the X-Forwarded-For header.
|
||||
*
|
||||
* Our edge proxy (Traefik) APPENDS the real peer IP to any client-supplied
|
||||
* chain, so the LAST hop is the only trustworthy entry. The first hop is
|
||||
* attacker-controlled: reading it lets bots rotate fake IPs per request and
|
||||
* defeat IP-based rate limiting entirely.
|
||||
*/
|
||||
export function parseClientIp(xForwardedFor: string | null | undefined): string | null {
|
||||
if (!xForwardedFor) {
|
||||
return null;
|
||||
}
|
||||
const hops = xForwardedFor
|
||||
.split(',')
|
||||
.map((hop) => hop.trim())
|
||||
.filter(Boolean);
|
||||
return hops.length > 0 ? hops[hops.length - 1] : null;
|
||||
}
|
||||
Reference in New Issue
Block a user