No Rate Limiting on API Endpoints
What Is This Vulnerability?
API endpoints without rate limiting allow unlimited requests from any client. This enables brute-force attacks on login forms, credential stuffing, API abuse, data scraping at scale, and denial-of-service conditions. Without throttling, a single attacker can overwhelm your backend or exhaust expensive third-party API quotas.
Why It Happens
Rate limiting is often treated as an optimization rather than a security requirement. Early-stage applications focus on functionality and skip throttling. Developers may rely on their cloud provider's DDoS protection without realizing it does not cover application-layer abuse. Microservice architectures make it unclear which service should enforce limits.
Example Code
import express from "express";
const app = express();
app.post("/api/login", async (req, res) => {
const { email, password } = req.body;
const user = await authenticate(email, password);
if (user) {
res.json({ token: generateToken(user) });
} else {
res.status(401).json({ error: "Invalid credentials" });
}
});import express from "express";
import rateLimit from "express-rate-limit";
const app = express();
const loginLimiter = rateLimit({
windowMs: 15 * 60 * 1000,
max: 10,
message: { error: "Too many login attempts, please try again later" },
standardHeaders: true,
legacyHeaders: false,
});
app.post("/api/login", loginLimiter, async (req, res) => {
const { email, password } = req.body;
const user = await authenticate(email, password);
if (user) {
res.json({ token: generateToken(user) });
} else {
res.status(401).json({ error: "Invalid credentials" });
}
});How Hackers Exploit It
Without rate limiting, attackers can launch automated credential stuffing attacks using lists of stolen passwords, trying thousands of combinations per minute. They can scrape entire databases through paginated API endpoints, exhaust paid third-party API quotas to cause financial damage, or simply flood endpoints to cause a denial of service.
How to Fix It
Implement rate limiting at multiple layers. Use a reverse proxy (nginx, Cloudflare) for global limits. Add application-level rate limiting with libraries like express-rate-limit or similar for your framework. Apply stricter limits on sensitive endpoints like login, registration, and password reset. Use sliding window algorithms and consider per-user, per-IP, and per-API-key limits.