Script Valley
JWT & Session Auth: Deep Dive
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

Rate Limiting

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.

Up next

HTTPS, HSTS, and secure headers for auth endpoints

Sign in to track progress

rate limiting login endpoints to prevent brute force attacks โ€” Security Hardening and Production Auth โ€” JWT & Session Auth: Deep Dive โ€” Script Valley โ€” Script Valley