Safeguard
Application Security•

ASVS 5.0 V1.3.12: Prevent Regular Expression DoS

ASVS V1.3.12 requires regexes free of exponential backtracking and bounded input. Find risky patterns, use linear time engines and test with long inputs.

Safeguard Security Team
3 min read

ASVS 5.0 V1.3.12 (Level 3) requires that regular expressions are free of elements that cause exponential backtracking, and that untrusted input is sanitized to mitigate ReDoS, also called runaway regex. A single crafted string of a few dozen characters can pin a CPU core for minutes (CWE-1333).

Why it matters

Backtracking engines (the default in JavaScript, Python, Java, Ruby and .NET) try alternative matches when a pattern fails. With nested quantifiers such as (a+)+$, the number of attempts doubles with each extra character. On a single threaded Node server, one request can stall everyone. The risky patterns are usually found in validators for emails, URLs and names, often copied from a forum.

How to implement it

Use these defenses together.

  1. Cap input length before matching. A name field never needs 100,000 characters.
  2. Prefer a linear time engine for untrusted input: RE2 (via re2 bindings), Rust's regex crate, Go's regexp, or the .NET RegexOptions.NonBacktracking option.
  3. Avoid nested quantifiers and overlapping alternations such as (a|aa)+.
  4. Set a match timeout where the runtime supports it, such as new Regex(pattern, options, TimeSpan.FromMilliseconds(100)) in .NET.
  5. Use a parser or a library validator for formats like email and URL instead of your own pattern.
import RE2 from 're2';

const re = new RE2('^[a-z0-9._-]+@[a-z0-9.-]+$', 'i');
function looksLikeEmail(s) {
  return typeof s === 'string' && s.length <= 254 && re.test(s);
}

How to test it

Run a ReDoS checker (such as safe-regex or a static analyzer) across your patterns, then test by hand. Feed 30, 40 and 50 repeated characters followed by one non matching character, like aaaa...a!. If time doubles with each added character, the pattern is vulnerable. Add the case to your test suite with a time limit.

Common mistakes

  • Validating length after the regex runs.
  • Trusting patterns copied from the web.
  • Testing only with valid input.
  • Assuming a timeout in the web server protects the process.

Where this goes wrong in practice

A common source is the email address validator. Developers search for a pattern that follows the RFC, paste it in and ship it. Some of those patterns contain nested repetition, and a string of a few dozen characters followed by a character that breaks the match stalls the thread. On Node, that thread serves all requests, so one attacker request is a full outage.

Another source is regexes in a WAF rule set, a log parser or a router, where the pattern is applied to every request. These have caused large public outages, because a single deployed rule made every server spend its CPU on matching.

Build ReDoS checks into your pipeline: lint patterns with a checker on every change, and run a short timing test against any pattern that processes untrusted text. Where a pattern is hard to make safe, replace it with code that splits and checks the pieces, which is slower to write and faster to run.

Quick check

What is the fastest fix for a vulnerable validator?

Limit the input length first, then move the pattern to a linear time engine or rewrite it without nested quantifiers.

How can you spot exponential behavior in a test?

Increase the input by one character at a time. If the match time doubles each step, backtracking is exponential.

Sources

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.