Without rate limiting, your login endpoint is vulnerable to brute force attacks. Day 4 implements rate limiting, account lockout, and password security.
npm install express-rate-limitconst rateLimit = require('express-rate-limit');
// General API rate limit
const apiLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 minutes
max: 100, // 100 requests per window
standardHeaders: true,
legacyHeaders: false,
message: { error: 'Too many requests, please try again later' }
});
// Strict limit for auth endpoints
const authLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 5, // 5 attempts per 15 minutes
skipSuccessfulRequests: true, // Don't count successful logins
handler: (req, res) => {
res.status(429).json({
error: 'Too many login attempts. Try again in 15 minutes.'
});
}
});
app.use('/api/', apiLimiter);
app.use('/login', authLimiter);
app.use('/register', authLimiter);const bcrypt = require('bcrypt');
const SALT_ROUNDS = 12; // Higher = slower to crack, slower to compute
// On registration: hash the password
async function register(email, plainPassword) {
const hash = await bcrypt.hash(plainPassword, SALT_ROUNDS);
await User.create({ email, passwordHash: hash });
}
// On login: compare password to hash
async function login(email, plainPassword) {
const user = await User.findByEmail(email);
if (!user) {
// Still compare to prevent timing attacks
await bcrypt.compare(plainPassword, '$2b$12$invalid.hash.padding');
return null;
}
const valid = await bcrypt.compare(plainPassword, user.passwordHash);
return valid ? user : null;
}
// Password strength validation
function isStrongPassword(password) {
return (
password.length >= 12 &&
/[A-Z]/.test(password) &&
/[0-9]/.test(password) &&
/[^A-Za-z0-9]/.test(password)
);
}