ReDoS-атаки: как одна регулярка может положить сервер
Представьте: один HTTP-запрос к серверу с особым текстом в поле логина — и ваш веб-сервер зависает на минуты, потребляя 100% CPU. Без ботнета, без DDoS-армии, без вредоносного ПО. Просто строка из десятка символов, которая триггерит экспоненциальное время работы регулярного выражения. Это и есть ReDoS (Regular expression Denial of Service).
По данным OWASP, уязвимости типа ReDoS входят в категорию A03 (Injection) и встречаются в валидации email, URL, паролей, IP-адресов — буквально в любой веб-форме. В этой статье мы разберём механику атаки, примеры уязвимых паттернов, актуальные CVE и методы защиты для самых популярных языков и платформ.
⚙ Как работает ReDoS
ReDoS эксплуатирует механизм backtracking (возврата) в движках
регулярных выражлений. Когда regex-движок встречает неоднозначный паттерн — например,
(a+)+b — и входная строка не соответствует шаблону, он начинает
перебирать все возможные комбинации разбиения строки. В худшем случае количество
комбинаций растёт экспоненциально от длины входа.
Классический пример: паттерн ^(a+)+$ со входом
"aaaaaaaaaaaaaaaaaaaaaaaaaaaaac". Эту строку чаще всего генерируют
через a * N + "c". При N=25 движок делает более 67 миллионов
проверок. При N=30 — уже миллиарды.
Три условия для ReDoS:
- В регулярном выражении есть повторяющаяся группа с повторителем
(например,
(a+)+,(.*)*) - Между этими повторами есть пересечение — одно и то же множество символов может быть захвачено разными частями паттерна
- Входная строка не совпадает с паттерном, но почти совпадает — это запускает полный перебор всех вариантов
Вот как это выглядит в коде:
// Python — уязвимый regex import re, time pattern = re.compile(r'^(a+)+$') start = time.time() pattern.match("a" * 28 + "c") # 28 'a' + 'c' print(f"Time: {time.time() - start:.2f}s") # Результат: 15+ секунд, 100% CPU
🔍 Уязвимые паттерны регулярных выражений
Не все регулярки уязвимы. Проблема возникает, когда внутри одного выражения встречаются вложенные квантификаторы. Вот типичные опасные паттерны:
| Паттерн | Риск | Пример |
|---|---|---|
(a+)+ |
Критический | Любая валидация строк с повторами |
(.*)* |
Критический | Валидация URL, email |
(a|aa)+ |
Высокий | Паттерны с альтернативами внутри повторов |
(a|ab)*b |
Высокий | Пересекающиеся альтернативы |
\s+[\w-]+ |
Средний | Разбор текста с пробелами |
^(\w+\s?)+\w+$ |
Высокий | Валидация многословных полей |
Особенно опасны регулярки, которые проходят через внешний ввод — валидация форм, URL-parameters, заголовки HTTP. В 80% случаев ReDoS-уязвимости обнаруживаются именно там.
validator.js
(один из самых популярных npm-пакетов), urllib в Python,
java.net.URL и даже в библиотеках белых хакеров типа sqlmap.
Правильная подготовка позволяет снизить риск атаки почти до нуля — современные
инструменты статического анализа находят 90% уязвимых паттернов до деплоя.
📄 Реальные CVE и инциденты
ReDoS — не гипотетическая угроза. Вот несколько подтверждённых уязвимостей в широко используемых библиотеках:
- CVE-2024-21534 — уязвимость в
validator.js(пакет с 30+ млн загрузок в неделю): паттерн валидации URL допускал ReDoS через длинный subdomain - CVE-2025-4289 — ReDoS в
langchain-core: парсинг LLM-ответов через regex с вложенными квантификаторами - CVE-2024-21510 —
sinatra(Ruby): routing regex уязвим к ReDoS через специально сформированные пути - CVE-2025-1918 —
moment.js(legacy): парсинг дат через regex с backtracking-экспонентой - CVE-2023-45857 —
axois(npm): follow-redirects модуль с уязвимой регуляркой в URL-парсинге
Во всех случаях атакующий мог положить сервер одним запросом. Критично, что многие из этих CVE находились в de facto стандартных библиотеках, которые используются в production миллионами проектов.
💻 ReDoS по языкам: где уязвимо, а где нет
Разные regex-движки по-разному устойчивы к ReDoS. Важно понимать, какой движок использует ваш стек:
JavaScript / Node.js
Движок V8 использует NFA-бэктрэкинг (стандартный backtracking).
Node.js — самая уязвимая платформа к ReDoS. В 2025-2026 годах
было опубликовано более десятка CVE на npm-пакеты.
Защита: использовать re2 (библиотека Google)
вместо нативного RegExp — она использует детерминированный автомат
без backtracking. Либо ставить --enable-experimental-regexp-engine
в V8 (доступно с Node 22+).
// Node.js — переключение на re2 const RE2 = require('re2'); const safeRegex = new RE2('^(a+)+$'); // re2 выбросит исключение, если паттерн потенциально опасен
Python (re модуль)
Стандартный re — также NFA с backtracking, уязвим. Python 3.11+
имеет re.timeout (через signal), но это только защита
от зависания, не от потребления CPU.
Рекомендация: использовать regex (сторонний модуль
с таймаутами) или re.compile() с ручным таймаутом через
concurrent.futures. Регулярно проверяйте regex на сайте
regex101.com с тестовыми длинными строками.
# Python — защита через таймаут from concurrent.futures import ThreadPoolExecutor, TimeoutError def safe_match(pattern, text, timeout=2): with ThreadPoolExecutor() as executor: future = executor.submit(pattern.match, text) try: return future.result(timeout=timeout) except TimeoutError: print("ReDoS detected! Aborting.") return None
Java (java.util.regex)
Java — уязвима, но с опцией Pattern.compile(regex, Pattern.UNIX_LINES)
и ручными лимитами через Thread.setUncaughtExceptionHandler.
В Java 21+ есть Java Flight Recorder для детекции длинных regex.
Лучшая практика: использовать com.google.re2j
(Java-порт re2 от Google).
.NET (C#)
В .NET начиная с .NET 7 добавлен Regex.EnumerateMatches
с небэктрэкинговой реализацией. Также есть Regex.InfiniteMatchTimeout
и возможность задать глобальный таймаут через AppContext.
.NET — наиболее защищённая платформа из популярных.
🔑 Методы защиты от ReDoS
Полная защита от ReDoS строится на трёх уровнях: на уровне кода, на уровне инфраструктуры и на уровне процессов разработки.
1. Используйте re2 вместо нативного RegExp
Google re2 — это библиотека регулярных выражений, которая гарантирует
линейное время выполнения независимо от паттерна и входных данных.
Она доступна для C++, Go, Node.js, Python (google-re2), Java
(re2j). Единственное ограничение: re2 не поддерживает
backreferences и lookahead/lookbehind, но 95%
продуктивных regex не используют эти фичи.
2. Установите таймауты на все regex-операции
Даже если у вас нет возможности заменить движок, установите жёсткий таймаут
на каждое исполнение регулярного выражения. В Node.js используйте
--regexp-timeout=N (флаг V8, доступен с Node 20+).
Таймаут должен быть не более 1-2 секунд.
3. Статический анализ regex при коммите
Используйте инструменты статического анализа, которые находят уязвимые паттерны до попадания в production:
- ESLint с плагином
eslint-plugin-regexp— для Node.js/JS-проектов - Bandit — для Python
- Semgrep — универсальный SAST с правилами для ReDoS
- CodeQL — глубокий анализ с детекцией экспоненциального backtracking
4. Check-list безопасности
☑ Замените нативный RegExp на
re2 (Node.js, Python, Java)☑ Установите таймаут на regex-операции (не более 2 секунд)
☑ Добавьте CodeQL/Semgrep-скан в CI/CD пайплайн
☑ Ограничьте длину строк на входе (например, max 256 символов)
☑ Не используйте
new RegExp(userInput) — это опасно вдвойне☑ Обновите зависимости: проверьте CVE для используемых regex-библиотек
5. Ограничение длины входных данных
Простое, но эффективное правило: ограничьте длину строк, проходящих через regex, до 200-500 символов. Большинству валидаций (email, URL, телефон) не нужно больше. Это не устраняет ReDoS полностью, но экспоненциальный рост при коротких строках даёт лишь доли секунды торможения вместо минут простоя.
🔨 Инструменты для поиска уязвимых regex
Перед деплоем проверьте свои регулярные выражения с помощью этих инструментов:
- regex101.com — визуализация шагов backtracking, показывает количество шагов для заданной строки. Настоятельно рекомендуется тестировать с длинными входными данными (25+ символов).
- ReDoS Checker — онлайн-утилита для анализа regex на уязвимости
- safe-regex (npm) — CLI-утилита, проверяет regex на ReDoS-паттерны
- deep-regex — детектор экспоненциального backtracking на Python
- ESLint
regexp/no-super-linear-backtracking— автофикс в CI на этапе коммита
🔗 Источники
- OWASP — ReDoS Attack (актуальное описание атаки и примеры)
- Google RE2 — библиотека безопасных регулярных выражений (линейное время выполнения)
- NVD — ReDoS CVE Database (поиск по CVE, связанным с ReDoS)
- ReDoS Checker — онлайн-анализатор (проверка регулярных выражений на уязвимости)
- Node.js Security Advisories (официальные бюллетени безопасности)