On this page
What Is ReDoS (Regular Expression Denial of Service)?
ReDoS is a denial-of-service attack that uses a badly written regular expression to burn CPU. Learn what catastrophic backtracking is and how to fix it.
Quick answer
- ReDoS (Regular Expression Denial of Service) is an availability attack: a small input string makes a badly written pattern run for minutes or hours.
- The cause is catastrophic backtracking — a pattern with nested quantifiers like
(a+)+or overlapping alternatives like(a|aa)+.- When the match ultimately fails, the engine has already tried an exponential number of ways to split the string, so work explodes from a few characters.
- Fix the pattern, then cap input length and time-box the match as a backstop.
What is ReDoS?
ReDoS (Regular Expression Denial of Service) is a denial-of-service attack in which an attacker sends a short, crafted string that causes a regular expression on your server to consume CPU for an unreasonable amount of time. The payload can be under a hundred characters. The processing it triggers can be measured in hours.
It is a pure availability attack: no data is stolen and no code is executed. The damage is that a request handler stops responding, which in a synchronous web server means it holds a worker, and enough such requests exhaust the pool. Because the string is short and the pattern is a normal-looking one, filters that limit request size do nothing.
Where this bites vibecoders
The specific way this bites vibecoders is validation. An assistant asked to “validate the username or email format” will often produce a pattern with nested repetition to make it flexible, and will happily apply the same helper to a search box, a slug generator, or a log parser. The function has a test, the test passes on
"alice@example.com", and the pattern is never exercised against a string designed to fail.
What is catastrophic backtracking?
Most regular expression engines use backtracking: they try a match, and when it fails, they undo part of the match and try a different arrangement. This is why patterns like (\d+)+$ are so convenient to write — the engine will explore until it finds an answer.
The problem arises when a pattern contains a group that can match the same text in many different ways, wrapped in a quantifier. On a string that ultimately fails to match, the engine must exhaust every arrangement before it can conclude. That number grows exponentially with input length.
// Vulnerable: (a+)+ — a quantified group whose contents can also repeat.
const vulnerable = /^(a+)+$/;A string of 30 a characters followed by a b looks like this to the engine:
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaaa
aaaaaaaaaaaaaaaaaaaaaaaaaaaa
...Each a can be matched by the outer group or the inner one, in every combination. Around 30 characters is already tens of thousands of attempts; the practical limit is often near 40–50 characters, and doubling the length squares the work.
The safe pattern either removes the nesting or makes the alternatives unable to overlap:
// Fixed: no nested quantifier, and the match is anchored.
const safe = /^a+$/;What patterns are vulnerable?
The risky shapes are predictable, which is why tooling can find them:
| Vulnerable shape | Why it backtracks | Safer alternative |
|---|---|---|
(a+)+ | Group repeats, and its contents also repeat | a+ |
(a|aa)+ | Alternatives overlap — a is a prefix of aa | a+ |
(\s*\w+)*$ | Repeated group allows many splits of the same run | Anchor and simplify; split the work |
^(\w+\s?)*$ | Same pattern, extremely common in “name” validation | ^\w+(\s\w+)*$ |
^(a|ab)(c|bcd)(d|.*)$ | Classic textbook overlap chain | Restructure to unambiguous alternatives |
The common thread is a repeated group whose alternatives can match the same text in more than one way. One nested quantifier or one overlapping pair is enough.
Where AI coding assistants get ReDoS wrong
- Over-flexible validation patterns. A “robust” email or name pattern built with
(\w+)*or(\s*\w+)*is the single most common generated vulnerable shape. - One shared validator applied everywhere. The same helper validated for one field gets reused on a search query, a filter, and a webhook body, multiplying the exposure.
- Unescaped user input interpolated into a pattern. Building a regex by string concatenation from a query parameter is both a ReDoS risk and a regex injection — see What Is Cross-Site Scripting (XSS)? for the same principle applied to markup.
- ReDoS never appears in dependency audit reports. Scanners do not analyse regular expressions, so a vulnerable pattern can sit in a lockfile-adjacent library for years.
- No regex execution timeout. Every call is synchronous on the event loop, so one slow match stalls unrelated requests in the same process.
Checklist
- Inventory every regular expression that runs on user input, including ones in dependencies and helpers.
- Remove nested quantifiers and make overlapping alternatives mutually exclusive.
- Anchor user-facing patterns with
^and$to cut the search space early. - Use possessive or atomic groups where the language supports them, or the built-in timeout option.
- Cap the length of any string you run a user-influenced pattern against.
- Run matching in a worker with a hard timeout for endpoints that accept arbitrary input.
- Add a lint rule for the vulnerable shapes and gate the build on it — see How to Add SAST Scanning to a GitHub Repo.
- Keep rate limiting on the affected endpoint as a backstop — see How to Add Rate Limiting to an API.
- Prefer doing the work without a regex when the task is simple enough to express as string methods.
FAQ
What is ReDoS in simple terms?
ReDoS (Regular Expression Denial of Service) is a way to make your server stop responding by sending it a short string that is pathologically slow to process against a particular pattern. The string is tiny, but the work required grows exponentially, so a few hundred characters can occupy a CPU core for minutes.
What causes catastrophic backtracking?
It happens when a pattern contains nested quantifiers or a quantified group whose alternatives overlap, such as (a+)+ or (a|aa)+. The engine tries every possible way to split the input across the repeated group, and when the match ultimately fails it has already tried an exponential number of them.
Is ReDoS a bug in the regex engine?
No. Most engines implement backtracking because it makes patterns far easier to write, and it is correct, just not always fast. The problem is a pattern that lets an attacker force the worst case. Safe engines and linters exist, but the responsibility sits with the pattern author.
How do I fix a vulnerable regular expression?
Remove the nested quantifier where possible, make the alternatives mutually exclusive, and anchor the pattern with ^ and $. Replace a greedy unbounded match inside a group with a possessive or atomic group where the language supports it. As a backstop, cap the input length and run the match on a worker with a timeout.
Can a ReDoS attack steal data?
No. ReDoS is an availability attack — its goal is to make your service slow or unresponsive, not to read anything. The real risk is indirect: while handlers are blocked, legitimate requests time out, and an outage on a public endpoint is its own kind of disclosure about your uptime.
How long does a ReDoS payload need to be?
Typically only a few dozen characters, which defeats any size-based limit and makes it look like ordinary traffic. The practical ceiling where most engines become unusable is often around 40–50 repeated characters, so a payload that fits in a query string is enough.
Related topics
- How to Add SAST Scanning to a GitHub Repo
- What Is a Race Condition?
- What Is Cross-Site Scripting (XSS)?
- How to Add Rate Limiting to an API
- The 15 Security Failures Your AI Coding Assistant Ships by Default