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).