fix(ratelimit): split global and login budgets so storms can't lock out login

One shared 100 req / 10 min limiter counted everything —
401s, CORS preflights, even /health — so a bad-token burst
from a second frontend burned the budget and 429'd POST
/api/auth/login too, leaving no way back in short of
waiting out the window or restarting (MemoryStore reset).

New middleware/rateLimit.js holds two limiters, verified
against express-rate-limit@5.5.1:

- apiLimiter (300 / 15 min) skips OPTIONS, /health, and the
  public auth endpoints, so preflights, probes, and login
  attempts never drain real API budget.
- authLimiter (20 / 15 min, successful logins free) guards
  register/login/forgotpassword/resetpassword against
  brute force. 429 there means 20 wrong passwords, never
  a busy API.

Storm-tested with live server: 320 bad-token hits burn the
global budget yet login still returns 200; 25 bad logins
trip only the login limiter while API traffic is untouched.
This commit is contained in:
2026-09-05 22:48:48 -04:00
parent 4fe5aa25f8
commit 39f1c88255
3 changed files with 44 additions and 11 deletions
+2 -7
View File
@@ -6,10 +6,10 @@ import helmet from 'helmet'
import cookieParser from 'cookie-parser'
import mongoSanitize from 'express-mongo-sanitize'
import xss from 'xss-clean'
import rateLimit from 'express-rate-limit'
import hpp from 'hpp'
import morgan from 'morgan'
import errorHandler from './middleware/error.js'
import { apiLimiter } from './middleware/rateLimit.js'
@@ -36,12 +36,7 @@ const corsOptions = {
},
}
const limiter = rateLimit({
windowMs: 10 * 60 * 1000, // 10 minutes
max: 100
})
app.use(express.json(), cookieParser(), morgan('dev'), mongoSanitize(), helmet(), xss(), limiter, hpp(), cors())
app.use(express.json(), cookieParser(), morgan('dev'), mongoSanitize(), helmet(), xss(), apiLimiter, hpp(), cors())
app.get('/health', (req, res) => res.status(200).json({ status: 'ok' }))