Add rate limiting on login and signup to prevent brute-force attacks#381
Conversation
/api/auth/login and /api/auth/signup had no request throttling, allowing unlimited automated password guessing with no server-side resistance. Add express-rate-limit with a 10-request per 15-minute per-IP window on both endpoints. skipSuccessfulRequests:true ensures only failed attempts count against the quota, so legitimate users who succeed on the first try are not penalised. The limiter is mounted after passport.session() but before the route handlers so it cannot be bypassed by any route-level code. Add express-rate-limit ^7.5.1 to package.json dependencies. Closes GitMetricsLab#372
✅ Deploy Preview for github-spy ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Warning Rate limit exceeded
You’ve run out of usage credits. Purchase more in the billing tab. ⌛ How to resolve this issue?After the wait time has elapsed, a review can be triggered using the We recommend that you space out your commits to avoid hitting the rate limit. 🚦 How do rate limits work?CodeRabbit enforces hourly rate limits for each developer per organization. Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout. Please see our FAQ for further information. ℹ️ Review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (2)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🎉🎉 Thank you for your contribution! Your PR #381 has been merged! 🎉🎉 |
What is the problem
/api/auth/loginand/api/auth/signuphave no request throttling. An attacker can issue unlimited automated requests against either endpoint:Neither endpoint has any lockout, delay, CAPTCHA, or IP-based throttle.
What was changed
backend/server.jsconst rateLimit = require('express-rate-limit')to the imports.authLimiter: 10 requests per 15-minute window per IP, withstandardHeaders: true(setsRateLimit-*headers per RFC 6585),legacyHeaders: false, a clear JSON error message, andskipSuccessfulRequests: trueso only failed attempts count against the quota.authLimiterto/api/auth/loginand/api/auth/signupindividually viaapp.use(), mounted afterpassport.session()and before the route handlers so it cannot be bypassed by any route-level code path.backend/package.jsonexpress-rate-limit: ^7.5.1as a production dependency.Why this approach
skipSuccessfulRequests: trueis essential for usability: a legitimate user who provides correct credentials on their first attempt does not consume quota and is never blocked. Only failed attempts deplete the window. This makes the limiter aggressive against automated attackers (who almost always fail) while being invisible to legitimate users.Mounting the limiter at the
app.use('/api/auth/login', authLimiter)level, rather than inside the route handler, means it applies before any authentication logic runs — the rate limit check is the very first thing that executes for these paths.The limiter block is placed after
passport.session()and before// Routes— a region with completely unique surrounding context that does not overlap with any other pending changes toserver.js, ensuring clean merges.How to test
POST /api/auth/loginrequests with wrong credentials from the same IP:{ "message": "Too many attempts, please try again after 15 minutes." }.RateLimit-Limit,RateLimit-Remaining, andRateLimit-Resetheaders appear on every response from these endpoints.Edge cases covered
skipSuccessfulRequests: trueprevents legitimate users from being locked out when they authenticate correctly.standardHeaders: truelets well-behaved clients read theRateLimit-*headers and back off before hitting the 429.loginandsignupindependently so neither endpoint can be used to exhaust the other's quota.legacyHeaders: falsesuppresses the deprecatedX-RateLimit-*headers to keep responses clean.Verification
server.jsare modified; the CORS block and session config are untouchedLabels:
type:securitylevel:intermediategssoc:approvedCloses #372
Please assign this PR to me under GSSoC 2026.