Security Hardening and Production AuthLesson 6.2
rate limiting login endpoints to prevent brute force attacks
brute force attack definition, express-rate-limit setup, per-IP limiting, per-account limiting, progressive delays, rate limit headers, Redis rate limit store
Rate Limiting Login Endpoints
Without rate limiting, an attacker can test thousands of passwords per second against your login endpoint. bcrypt slows each attempt, but parallelism across IPs still makes brute force feasible.
const rateLimit = require('express-rate-limit');
// Per-IP rate limit for login
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 10, // 10 attempts per IP per window
message: { error: 'Too many login attempts. Try again in 15 minutes.' },
standardHeaders: true, // Return RateLimit-* headers
legacyHeaders: false
});
app.post('/auth/login', loginLimiter, loginHandler);
Per-IP limiting alone is bypassable with IP rotation. Add per-account limiting by tracking failed attempts in Redis keyed by email:
async function checkAccountLockout(email) {
const key = `login:attempts:${email}`;
const attempts = await redis.incr(key);
if (attempts === 1) await redis.expire(key, 900); // 15 min TTL on first hit
if (attempts > 5) throw new Error('Account temporarily locked');
}
Return 429 Too Many Requests with a Retry-After header. Do not return different errors for valid vs invalid accounts โ error message differences allow username enumeration.
