If your login, password-reset, or verification endpoint accepts unlimited attempts, an attacker can try thousands of passwords or codes per minute until one works. Rate limiting caps how many attempts an IP or account can make in a window, which turns an easy brute-force into an impractical one. Add it to every authentication route.
Why it's a problem
Without a limit, a six-digit reset code has only a million possibilities and can be exhausted quickly, and weak passwords fall to a dictionary in seconds. The endpoint works normally for real users, so the missing limit is invisible until someone points an automated tool at it.
The pattern
// unlimited attempts
app.post("/login", async (req, res) => {
const ok = await checkPassword(req.body);
res.json({ ok });
});The fix
import rateLimit from "express-rate-limit";
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000, // 15 min
max: 10, // per IP
});
app.post("/login", loginLimiter, async (req, res) => {
const ok = await checkPassword(req.body);
res.json({ ok });
});Why AI tools write this
Generating a working login route answers the request as asked; rate limiting was not part of it. The endpoint authenticates correctly in testing, so the absence of a limit does not surface until it is attacked.
The quick fix
- Add rate limiting to login, password-reset, and verification endpoints.
- Limit by IP and, where possible, by account.
- Consider a short lockout or increasing delay after repeated failures.