API Rate Limiting: Token Bucket Algorithms & Anti-Brute Force Defense

Exposing unrestricted API endpoints without rate limiting is an open invitation to automated abuse. Without rate limits, adversaries deploy automated multi-threaded scripts to brute force user passwords, scrape proprietary catalog databases, exhaust third-party SMS billing credits, or launch application-layer denial-of-service floods. Rate Limiting and Throttling enforces consumption boundaries, ensuring service availability and stability.

Rate Limiting Algorithms Compared

Algorithm How It Works Pros & Trade-offs
Token Bucket Tokens are added to a bucket at a fixed constant rate. Each API request consumes one token. If the bucket is empty, requests are rejected (HTTP 429). Allows short bursts of traffic up to the bucket capacity while maintaining a stable long-term average.
Leaky Bucket Requests enter a queue (bucket) and are processed at a smooth, constant output rate (like water leaking from a hole). Smooths out traffic spikes; but drops requests if the queue overflows during sudden bursts.
Sliding Window Log Records timestamps of each request in a sorted set (e.g. Redis). Sums requests in the last 60 seconds dynamically. Most accurate! Eliminates boundary burst vulnerabilities; requires slightly more memory.

Standard HTTP 429 Response Headers

When a client exceeds permitted limits, the server should respond with HTTP 429 Too Many Requests and emit informative RFC 6585 headers:

HTTP/1.1 429 Too Many Requests
Retry-After: 60
X-RateLimit-Limit: 100
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1799880060
Content-Type: application/json

{ "error": "Rate limit exceeded. Please wait 60 seconds before retrying." }

Production Redis-Backed Rate Limiting in Express (Node.js)

const rateLimit = require('express-rate-limit');
const RedisStore = require('rate-limit-redis');
const { createClient } = require('redis');

const redisClient = createClient({ url: 'redis://localhost:6379' });
redisClient.connect();

// Strict limiter for authentication login route (5 attempts per 15 minutes)
const loginLimiter = rateLimit({
  store: new RedisStore({
    sendCommand: (...args) => redisClient.sendCommand(args),
  }),
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 5,                   // Limit each IP to 5 requests per windowMs
  standardHeaders: true,
  legacyHeaders: false,
  message: { error: 'Too many login attempts. Account temporarily throttled for 15 minutes.' }
});

app.post('/api/auth/login', loginLimiter, handleLogin);

Frequently Asked Questions (FAQ) - Student & Interview Review

Q1. Why should rate limits be keyed on both IP and User ID?

If you only rate limit by IP address, an attacker using a distributed proxy botnet (thousands of residential rotating IPs) bypasses limits. If you only rate limit by User ID, an unauthenticated attacker can flood your login endpoints. Keying on both provides comprehensive protection.

Q2. What is Cloudflare Turnstile / reCAPTCHA v3?

Invisible challenge systems that analyze non-interactive browser telemetry (mouse movements, browser headers, canvas rendering) to detect bots without forcing human users to solve frustrating picture puzzles.

Explore Related Cybersecurity Tutorials & Next Steps

Broaden your defensive and offensive security knowledge across our curriculum:

DoS/DDoS Attacks & Mitigation

Defending against application-layer HTTP floods with algorithmic rate limiters.

Session Security & Login Protection

Throttling failed authentication attempts to halt credential stuffing.

OWASP API Security Top 10

Remediating API4:2023 Unrestricted Resource Consumption with token buckets.

Secure Node.js (express-rate-limit)

Production code configuring Redis-backed rate limiting in Node.js applications.

Share Share on Facebook Share on Twitter Share on LinkedIn Pin on Pinterest Share on Reddit

You may also like this!