flawopen.com/¿Qué es ReDoS/Explained
¿Qué es ReDoS (retroceso catastrófico)?
Un motor regex con retroceso puede probar distintas divisiones de la misma entrada cuando falla una coincidencia. En algunos patrones ambiguos, los intentos crecen exponencialmente con la entrada. La demora depende de la regex, el motor, el tamaño de entrada, la concurrencia y los límites de recursos; las solicitudes costosas repetidas pueden agotar el servicio.
# Nested quantifier ^(a+)+$ # Overlapping alternatives, quantified ^(a|aa)+$ # A long matching prefix plus a final mismatch can # force extensive retries in vulnerable engines. # Do not benchmark this with attacker-sized input.
# Bound input before matching, then use a simple # full-string identifier rule with no nested ambiguity. import re IDENTIFIER = re.compile(r"[a-z0-9]{1,64}\Z") def valid_identifier(value: str) -> bool: if not isinstance(value, str) or len(value) > 64: return False return IDENTIFIER.fullmatch(value) is not None # A bound reduces worst-case work; still set request # size, concurrency, and execution-time limits.
Por qué se dispara el retroceso
Un patrón como ^(a+)+$ es ambiguo en un motor con retroceso: aaaa puede dividirse de varias maneras entre cuantificadores anidados. Un carácter final que no coincide puede hacer que el motor explore alternativas antes de rechazar toda la cadena.
En patrones con esa ambigüedad, las rutas exploradas pueden crecer exponencialmente con la longitud del prefijo coincidente antes del fallo. Depende de la combinación concreta de patrón y motor, no de todas las regex o fallos. Las entradas cortas también importan con solicitudes concurrentes: limite tamaño, concurrencia y tiempo.
Dónde llegan entradas no confiables a una regex
La validación es un caso evidente, pero hay riesgos menos visibles: procesamiento de User-Agent y otros encabezados, registros, Markdown y resaltado de sintaxis, enrutamiento de URL, filtros de contenido y reglas WAF, además de bibliotecas que aplican regex a campos recibidos. Paquetes populares han publicado avisos ReDoS; las dependencias importan tanto como los patrones propios.
Preguntas frecuentes
El riesgo depende del motor y del patrón, no solo del lenguaje. Motores comunes con retroceso pueden explorar demasiadas alternativas en ciertos patrones. La documentación de regexp de Go garantiza tiempo lineal respecto a la entrada. La crate regex de Rust documenta O(m·n) por búsqueda (m es el tamaño del patrón y n el de la entrada), lineal en n para un patrón fijo; los iteradores de búsquedas repetidas tienen otros límites. Estos motores omiten funciones como backreferences que impiden esas garantías.
Un límite restringe el trabajo, pero el valor seguro depende del patrón, motor, tasa de solicitudes y presupuesto del servicio. Corrija los patrones ambiguos y limite antes de analizar o comparar la entrada.
Use un analizador estático con detección ReDoS y revise los resultados según el motor real. Busque grupos repetidos con cuantificadores o alternativas de prefijos solapados; pruebe con entradas acotadas y timeout estricto en un entorno aislado.
El soporte de timeout depende del runtime y de la API. En .NET, defina un timeout explícito para coincidencias con entradas no confiables. En otros entornos, consulte los controles documentados del motor; use uno más seguro o rediseñe el patrón si no hay timeout aplicable. El timeout complementa los límites de entrada y concurrencia, pero no vuelve eficiente un patrón ambiguo.
Referencias
- CWE-1333 — Inefficient Regular Expression Complexity
- OWASP — regular expression denial of service
- Go regexp package documentation
- Rust regex crate — performance and complexity
- .NET regular expression best practices
- Russ Cox — regular expression matching can be simple and fast
- Is JavaScript's RegExp vulnerable to ReDoS?