FlawOpen

flawopen.com/ReDoS/JavaScript RegExp

Уязвим ли JavaScript RegExp к ReDoS?

CWE-1333: Inefficient Regular Expression ComplexityСправочная страница
Краткий ответ

Представьте, что сотрудник пробует все способы разделить стопку бланков, когда на последнем нет отметки: неоднозначный шаблон заставляет regex-движок снова проверять одни и те же символы. Обычный путь RegExp в V8 может возвращаться назад; ограничение ввода и предсказуемый шаблон ограничивают работу. В Node синхронное сопоставление выполняется в потоке event loop и задерживает другие callbacks процесса. V8 описывает экспериментальный движок без backtracking, включаемый флагами и ограниченный по шаблонам; не считайте, что он включён в production.

УЯЗВИМО
// Неоднозначное вложенное повторение в данных запроса
const re = /^(\w+\s?)*$/;

app.post('/search', (req, res) => {
  const q = req.body.q;
  if (!re.test(q)) return res.sendStatus(400);
  res.sendStatus(200);
});
ИСПРАВЛЕНО
// Ограничьте ввод и принимайте только грамматику поля
app.post('/search', (req, res) => {
  const q = req.body.q;
  if (typeof q !== 'string' || q.length > 128)
    return res.sendStatus(400);

  // Пример: только ASCII-буквы слова и пробелы
  if (!/^[\w ]{1,128}$/.test(q))
    return res.sendStatus(400);

  res.sendStatus(200);
});

Почему Node.js усиливает последствия ReDoS

Синхронное сопоставление regex выполняется в потоке JavaScript, обслуживающем callbacks процесса Node.js. Если шаблон долго обрабатывает почти подходящую или не подходящую строку, event loop не может запускать другие callbacks и продвигать запросы, пока сопоставление не завершится. Workers, несколько процессов или реплик могут сохранить часть мощности, но затронутый процесс всё равно зависает.

Риск зависит от достижимости, сложности шаблона, ограничений ввода и топологии. Атакующий должен передать достижимому сопоставлению ввод, запускающий дорогой путь. Рассматривайте это как риск доступности затронутого процесса; отказ всего сервиса зависит от workers, реплик, маршрутизации и проверок здоровья.

Реальный случай npm: CVE-2026-83606 в xmldom

Проверенное уведомление для @xmldom/xmldom описывает CWE-1333 при разборе processing instruction: уязвимая грамматика позволяет жадной группе пробелов и ленивой группе данных поглощать одни пробелы. Если нет закрывающего ?>, парсер перебирает множество разбиений и работает квадратичное время. Затронуты версии 0.9.0-beta.9–0.9.10; в 0.9.11 разделитель перестал возвращать пробелы группе данных. Уведомление ограничивает проблему scoped-путём версии 0.9.x; ветка 0.8.x и пакет без scope не указаны как уязвимые.

Сканирование зависимостей полезно, но не доказывает безопасность regex

npm audit сообщает об известных уведомлениях для зависимостей в дереве проекта; он не анализирует каждое regex в коде и не обнаруживает неопубликованные ReDoS. Обновляйте прямые и транзитивные зависимости, проверяйте достижимость уязвимого кода и отдельно анализируйте шаблоны, обрабатывающие пользовательский текст.

npm audit --omit=dev

Аудит охватывает известные уведомления, а не все риски regex. Оцените отчёт с учётом развёртывания и отдельно проверьте, может ли недоверенный ввод вызвать дорогой матч и ограничена ли его длина.

Частые вопросы

Можно ли задать тайм-аут для JavaScript regex?

У RegExp API нет тайм-аута на одно сопоставление. Используйте простой шаблон и ограничьте ввод до запуска. Если нужна сложная обработка, изолируйте её в worker или дочернем процессе с принудительным сроком завершения; это ограничит последствия, но не ускорит шаблон.

Это касается браузерного JavaScript?

Да. Проблемное синхронное сопоставление может заморозить главный поток вкладки до завершения и заблокировать интерфейс. Ограничивайте ввод, упрощайте шаблон и не запускайте дорогой матч при каждом нажатии клавиши.

Безопасно ли использовать new RegExp(userInput) для поиска?

Считайте пользовательские шаблоны исполняемым вводом для потенциально дорогого сопоставления. Не принимайте произвольные шаблоны на сервере без строгих ограничений и изоляции. Если нужен буквальный поиск, экранируйте текст как regex-литерал или используйте API поиска по строке; экранирование не заменяет лимит длины.

Источники и исправления

См. также Что такое ReDoS (катастрофический возврат)?