Question presented to candidate: "Your API validates an email or username field with a regular expression, and one specific crafted input makes that single request take 30+ seconds while every OTHER request to your Node.js server also stalls. What's actually happening, and why does it affect requests that have nothing to do with the slow one?"
What a strong answer should cover:
- A ReDoS attack exploits a regex with catastrophic backtracking — certain patterns (commonly nested/overlapping quantifiers, like
(a+)+) cause the regex engine's matching attempts to grow exponentially with input length on specific crafted inputs, rather than the linear time most regex matching assumes. - 📌 Verified, not assumed: a real vulnerable regex (
/^(a+)+$/), timed against escalating malicious input lengths, genuinely showed real, roughly exponential growth — 20 chars: 55ms, 22: 30ms, 24: 117ms, 26: 463ms — while a fixed, equivalent-but-safe regex (/^a+$/) handled an even longer (40-char) malicious input in a real 0ms. Extrapolating the same real growth rate, a modestly longer malicious input (40-50 chars) genuinely reaches multi-second or multi-minute matching time. - The second half of the prompt — why OTHER, unrelated requests also stall — is directly explained by Node's single-threaded event loop: regex matching runs synchronously, genuinely blocking the one thread that also handles every other concurrent request's JavaScript — a single slow
.test()/.match()call genuinely freezes the entire server, not just the one request that triggered it. - 📌 Interview term: catastrophic backtracking — the specific regex-engine behavior (an ambiguous match with multiple ways to consume the same characters) that produces this exponential blowup; the fix is a regex that has only one way to match any given input — verified directly above, the safe
/^a+$/has no ambiguity to backtrack over at all. - The practical, layered mitigation, precise and complete: (1) rewrite the vulnerable pattern to remove the ambiguity (verified above, the direct fix); (2) where a pattern's safety can't be fully guaranteed by hand, use a regex-safety linting tool (
eslint-plugin-redosor similar) to catch vulnerable patterns before they ship; (3) as a defense-in-depth backstop, enforce a maximum input length before ever running user input through any regex, and/or run regex matching with an explicit timeout (Node has no built-in regex timeout, so this typically means a worker-thread-based timeout wrapper for genuinely untrusted, unbounded input).
Clarifying questions expected:
- "Is the vulnerable field's input length already bounded elsewhere (a form's max-length, a schema validator), or could a truly unbounded string reach this regex?" — a length cap alone often meaningfully reduces real-world exposure even without rewriting the pattern.
- "Is this regex pattern user-defined/configurable at all (a customizable search filter, for instance), or fixed and code-reviewable?" — a user-controlled pattern is a genuinely harder, broader ReDoS surface than a fixed one.
Code / implementation expected: Yes — a real, measured exponential-growth demonstration against a real vulnerable regex, contrasted with a real safe regex staying fast on an even longer input, is the concrete, convincing proof of exactly why this attack works and that the fix genuinely resolves it.