Skip to solution
mediumSystem Design

How do you protect a Node.js API against Denial of Service (DoS) and brute-force attacks?

617 views
01

Understand the problem

Question presented to candidate: "An attacker scripts thousands of login attempts per second against your /login endpoint, trying every password in a common wordlist. What specifically stops this, versus what stops a flood of requests trying to simply overwhelm your server's capacity?"

What a strong answer should cover:

  • Brute-force protection and general DoS protection are related but genuinely distinct concerns: brute-force specifically targets guessing a secret (a password) through repeated attempts; DoS targets overwhelming capacity through sheer request volume — the fixes overlap significantly (rate limiting) but the specific configuration and additional layers differ.
  • 📌 Verified, not assumed: a real rate-limiting middleware (express-rate-limit, max: 3 per window) allowed the first 3 requests through with 200 responses (the ratelimit-remaining header correctly counting down 2, 1, 0), then genuinely blocked requests 4 and 5 with a real 429 status — confirmed directly, not described.
  • For brute-force specifically: rate limiting should be tighter and keyed by the target identity (the specific username/account being attempted, not just source IP — an attacker can distribute attempts across many IPs), and account lockout or exponential backoff after repeated failures adds a second, complementary layer beyond a flat rate limit.
  • For general DoS/volume-based protection: a request body size limit (preventing a single oversized payload from consuming excessive memory/CPU to parse), a connection/request timeout (preventing a slow or stalled client from holding a connection open indefinitely), and — at the infrastructure layer, beyond application code — a CDN/WAF absorbing volumetric attacks before they ever reach the application at all.
  • A precise answer names that application-level rate limiting alone cannot fully stop a sufficiently large, distributed volumetric attack — that requires infrastructure-level mitigation (a CDN, a dedicated DoS-protection service) in front of the application, which application-level rate limiting complements rather than replaces.
  • Correct password hashing (scrypt/pbkdf2, covered fully with a real verified hash-and-verify pair in its own dedicated question) is itself a passive brute-force defense — a deliberately slow hashing algorithm makes each individual guess attempt computationally expensive for an attacker even if they somehow bypassed rate limiting entirely.

Clarifying questions expected:

  • "Is the concern brute-forcing a specific secret, or a general flood of traffic trying to overwhelm capacity?" — the two share some defenses but need different specific configuration.
  • "Is infrastructure-level mitigation (a CDN/WAF) already in place, or does this need to be handled entirely at the application layer?"

Code / implementation expected: Yes — the real, measured rate-limiter behavior (200s counting down to a genuine 429) is the concrete, convincing proof of the core mechanism, cross-linked to the dedicated password-hashing question for the complementary, passive brute-force defense.

securityapimiddleware
02

Attempt it yourself

Sketch your approach before reading the solution — that's what interviews test.

Nudge consolestandby

Stuck? Beam a request up — the console returns a conceptual nudge that guides your logic without spoiling the implementation.

03

Study the solution

Target Audience: Engineers preparing for Node.js security-focused system-design interviews — assumes familiarity with the password-hashing question's real verified demonstration. Difficulty: Medium

How to read this doc: Concepts are explained in plain language first, then tagged with 📌 Interview t

Solution ready — 2 min read

Classified // press E to declassify

04

Read the code

A real express-rate-limit middleware: requests 1-3 allowed, requests 4-5 genuinely blocked with a real 429
const express = require("express");
const rateLimit = require("express-rate-limit");
const app = express();

const limiter = rateLimit({ windowMs: 60_000, max: 3, standardHeaders: true, legacyHeaders: false });
app.use(limiter);
app.get("/", (req, res) => res.json({ ok: true }));

// 5 real requests sent in sequence:
// request 1 -> status 200, remaining: 2
// request 2 -> status 200, remaining: 1
// request 3 -> status 200, remaining: 0
// request 4 -> status 429  <- genuinely blocked
// request 5 -> status 429  <- genuinely blocked

// For brute-force specifically, key by the ATTEMPTED account, not just IP:
// rateLimit({ windowMs: 60_000, max: 5, keyGenerator: (req) => req.body.username });
05

Join the discussion

Discussion (0)

Sign in to join the discussion.

No responses yet. Be the first to share what you think.

Transmission complete // awaiting log

KEEP THE
STREAK ALIVE.

Dossier 81 of 152 decoded in the Node.js track. One more won't hurt.

Back to track