fix(auth): add account+IP brute-force rate limiting

This commit is contained in:
Adolfo Reyna
2026-02-20 21:20:21 -05:00
parent 469962d03c
commit 19d805d322
2 changed files with 121 additions and 8 deletions

View File

@@ -9,6 +9,7 @@ require('dotenv').config();
const express = require('express');
const app = express();
const port = process.env.PORT || 3000;
app.set('trust proxy', true);
// -- Accept request from other origins
const cors = require('cors');
const { corsOptions } = require('./config/corsOptions');
@@ -34,15 +35,11 @@ const limiter = rateLimit({
return ip.includes(":") ? ip.split(":")[0] : ip; // Remove port if present
}
});
app.set('trust proxy', true);
app.use(limiter);
// SECURITY PLAN (point #3):
// Add dedicated auth limiter(s) for /login and password reset endpoints.
// Use tighter thresholds than the global limiter and key by account+IP.
// Authentication
const { signup, login, logout, resetPassword, loginWithPasswordToken } = require('./auth/authEmail.js');
const { authRateLimiter } = require('./middleware/authRateLimiter');
/**
* @swagger
* /signup:
@@ -110,7 +107,7 @@ app.post('/signup', signup);
* description: Invalid credentials.
*/
// SECURITY FIX (#2): POST-only login to avoid query-string credential leakage.
app.post('/login', login);
app.post('/login', authRateLimiter('login'), login);
/**
* @swagger
* /logout:
@@ -158,7 +155,7 @@ app.get('/logout', logout);
* 400:
* description: Bad request.
*/
app.route('/resetPassword').post(resetPassword);
app.route('/resetPassword').post(authRateLimiter('reset'), resetPassword);
// SECURITY FIX (#1):
// Single-use token login endpoint for password recovery flow.
/**
@@ -182,7 +179,7 @@ app.route('/resetPassword').post(resetPassword);
* 401:
* description: Invalid or expired token
*/
app.post('/password/token-login', loginWithPasswordToken);
app.post('/password/token-login', authRateLimiter('token'), loginWithPasswordToken);
// Routes
const profileRoute = require('./routes/profile.js');