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