← All fixes

Fix it

No rate limiting on login (brute force) — how to fix it

Medium severityCWE-307 (Improper Restriction of Excessive Authentication Attempts)

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.

Want to know if this pattern is already in a repo you shipped? Scan a public repo free, no account needed.

Scan a repo →
No Rate Limiting on Auth Routes (Brute Force) — How to Fix It: Prbl