JAVASCRIPT

Mitigating Regular Expression Denial of Service (ReDoS) Attacks

Learn to identify and prevent Regular Expression Denial of Service (ReDoS) vulnerabilities by crafting efficient and secure regex patterns in JavaScript.

// Example of a potentially vulnerable regex (catastrophic backtracking)
// Avoid nested quantifiers like (a+)+ or (a|b|c)* without care
const vulnerableRegex = /^(a+)+$/; // Matches one or more 'a's, repeated one or more times

function testVulnerableRegex(input) {
  console.log(`Testing vulnerable regex with input: "${input}"`);
  const start = performance.now();
  try {
    const match = vulnerableRegex.test(input);
    const end = performance.now();
    console.log(`  Match: ${match}, Time: ${end - start}ms`);
  } catch (e) {
    console.error(`  Error: ${e.message}`); // Regex can time out in some engines/libs
  }
}

// Inputs that can cause ReDoS:
testVulnerableRegex("aaaaaaaaaaaaaaaaaaaaaaaa!"); // Long string, ending with a non-matching char
testVulnerableRegex("aaaaaaaaaaaaaaaaaaaaaaaaa"); // Long matching string is fine
// testVulnerableRegex("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!"); // Uncomment with caution, might freeze tab

// --- SECURE ALTERNATIVES ---

// 1. Simpler, non-backtracking regex for the same intent
// (a+)+ is equivalent to a+
const safeRegex1 = /^a+$/;

function testSafeRegex1(input) {
  console.log(`Testing safe regex 1 with input: "${input}"`);
  const start = performance.now();
  const match = safeRegex1.test(input);
  const end = performance.now();
  console.log(`  Match: ${match}, Time: ${end - start}ms`);
}

testSafeRegex1("aaaaaaaaaaaaaaaaaaaaaaaa!");
testSafeRegex1("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!");

// 2. Using a library like 'safe-regex' or 're2' (if available for JS/Node.js)
/*
const safeRegex = require('safe-regex');
const dangerousPattern = '^(a+)+$';
if (!safeRegex(dangerousPattern)) {
  console.warn(`Pattern "${dangerousPattern}" is potentially vulnerable to ReDoS.`);
  // Refuse to use or suggest a safer alternative
} else {
  console.log(`Pattern "${dangerousPattern}" is safe.`);
  // Proceed to use it
}
*/

// 3. For more complex cases, avoid overly complex regex or break them down.
// Example: Validating a simple URL structure with a potentially complex regex
// A simpler approach might involve multiple checks or specific URL parsing libraries.
// Instead of: /^((?!-)[A-Za-z0-9-]{1,63}(?<!-)\.)+[A-Za-z]{2,6}$/ (complex and potentially vulnerable)
// Consider: Use URL object for parsing, then validate parts
function isValidDomain(domain) {
  // A reasonable regex, avoiding catastrophic backtracking
  const domainRegex = /^[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.[a-zA-Z]{2,6}$/;
  return domainRegex.test(domain);
}

console.log("Testing domain validation:");
console.log(`  example.com: ${isValidDomain("example.com")}`);
console.log(`  sub.example.co.uk: ${isValidDomain("sub.example.co.uk")}`);
console.log(`  long-domain-name-with-many-parts.com: ${isValidDomain("long-domain-name-with-many-parts.com")}`);
// Example that would be slow for a vulnerable regex but is fast for a safe one
const longMaliciousDomainAttempt = "a".repeat(100) + "!.com";
console.log(`  ${longMaliciousDomainAttempt}: ${isValidDomain(longMaliciousDomainAttempt)}`);
How it works: Regular Expression Denial of Service (ReDoS) occurs when a poorly constructed regular expression takes an exponentially long time to process certain input strings, potentially tying up server resources and causing a denial of service. This snippet demonstrates a common ReDoS pattern, "catastrophic backtracking," and provides safer alternatives. Key strategies include simplifying regex patterns, avoiding nested quantifiers on the same or overlapping sub-expressions, and considering external libraries that analyze regex safety or use more efficient regex engines (like RE2).

Need help integrating this into your project?

Our team of expert developers can help you build your custom application from scratch.

Hire DigitalCodeLabs