FlawOpen

flawopen.com/¿Qué es ReDoS/Explained

¿Qué es ReDoS (retroceso catastrófico)?

CWE-1333: Inefficient Regular Expression ComplexityPágina de referencia
Respuesta breve

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.

PATRONES VULNERABLES
# 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.
MÁS SEGURO
# 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

Which languages are affected?

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.

Is a length limit enough on its own?

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.

How do I find vulnerable patterns?

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.

Can I add a timeout instead?

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

Véase también ReDoS en Node.js: regex y CVE-2026-83606