ReDoS — Regular Expression DoS

ReDoS (Regular Expression Denial of Service) — атака, при которой специально сформированный ввод вызывает катастрофическое обратное отслеживание (backtracking) в регулярном выражении, заставляя CPU работать экспоненциально долго.

Зачем нужно

Одиночный HTTP-запрос со специальной строкой может заморозить Node.js event loop на секунды, делая сервис недоступным. Уязвимые regex встречаются в валидаторах email, URL, дат — популярных библиотеках (validator.js, moment.js уязвимых версий).

Где используется

  • Валидация email, URL, IP-адресов, HTML через regex
  • Парсинг логов и входных данных форм
  • Template engines с regex-парсингом

Основной контент

Классический уязвимый паттерн — backtracking

// УЯЗВИМО — вложенные квантификаторы
const vulnerable = /^(a+)+$/;

// Нормальная строка: "aaa" — быстро
console.time('safe');
vulnerable.test('aaa');
console.timeEnd('safe'); // ~0.01ms

// Атака: "aaaaaaaaaaaaaaaaaaaaaaaaaaa!" — зависание
console.time('attack');
vulnerable.test('aaaaaaaaaaaaaaaaaaaaaaaaaaa!'); // может занять минуты!
console.timeEnd('attack');

Уязвимые паттерны (антипаттерны regex)

// Паттерны, вызывающие catastrophic backtracking:
/^(a|aa)+$/         // Альтернации с перекрытием
/^(\w+\s?)*$/       // Вложенные квантификаторы
/(a+)+/             // Nested quantifiers
/^([a-zA-Z0-9])*\s*([a-zA-Z0-9])*$/  // Двойные квантификаторы

Безопасные альтернативы

// Атомарные группы и possessive quantifiers (Node 16+ поддерживает atomic)
// Или переписать без вложенности:

// УЯЗВИМО: валидация email через regex
const badEmailRegex = /^([a-zA-Z0-9_\.-]+)@([a-zA-Z0-9_\.-]+)\.([a-zA-Z\.]{2,6})$/;

// БЕЗОПАСНО: использовать готовую проверенную библиотеку
const isEmail = require('validator').isEmail;
isEmail(userInput); // Безопасная реализация

Timeout для regex (Node.js v18.19+)

// Использовать worker_threads для изоляции regex с таймаутом
const { Worker, isMainThread, parentPort, workerData } = require('worker_threads');

function testWithTimeout(pattern, input, timeoutMs = 100) {
  return new Promise((resolve, reject) => {
    const worker = new Worker(`
      const { parentPort, workerData } = require('worker_threads');
      const re = new RegExp(workerData.pattern);
      parentPort.postMessage(re.test(workerData.input));
    `, { eval: true, workerData: { pattern, input } });

    const timer = setTimeout(() => {
      worker.terminate;
      reject(new Error('ReDoS detected: regex timeout'));
    }, timeoutMs);

    worker.on('message', (result) => { clearTimeout(timer); resolve(result); });
  });
}

Инструменты для обнаружения уязвимых regex

# vuln-regex-detector
npx vuln-regex-detector --pattern "^(a+)+$"

# safe-regex (npm)
npx safe-regex "^(a+)+$"
# → false (небезопасно)

Частые ошибки

  • Самописные regex для валидации email/URL вместо проверенных библиотек
  • Использование .* с последующими квантификаторами
  • Альтернации (x|xy)+ с перекрывающимися вариантами
  • Отсутствие максимальной длины входных данных перед применением regex

Связанные темы

Ресурсы