Aikido

Protección contra expresiones regulares lentas: prevención de ataques ReDoS

Legibilidad

Regla
Proteger contra lento regulares lentas.
Expresiones regulares regulares con anidadas anidados o 
ambiguos ambiguos pueden causar catastróficas 
retroceso y rendimiento rendimiento.
Idiomas compatibles: 45+

Introducción

Las expresiones regulares pueden congelar su aplicación durante segundos o minutos con la entrada correcta. El retroceso catastrófico se produce cuando los motores de expresiones regulares exploran caminos que aumentan exponencialmente al intentar coincidir con un patrón. Una expresión regular como (a+)+b tarda microsegundos en coincidir con una entrada válida, pero puede tardar horas en rechazar una cadena de a's sin una b al final. Los atacantes se aprovechan de esto mediante ataques de denegación de servicio de expresiones regulares (ReDoS), enviando una entrada manipulada que hace que el motor regex consuma el 100% de la CPU hasta que se agota el tiempo de espera de la solicitud o el proceso se bloquea.

Por qué es importante

Implicaciones para la seguridad (ataques ReDoS): Un atacante puede paralizar su aplicación con una sola petición que contenga una entrada manipulada. La validación del correo electrónico y los patrones de análisis de URL son objetivos habituales. A diferencia de los ataques DoS tradicionales que requieren ancho de banda, ReDoS sólo necesita pequeñas cargas útiles.

Disminución del rendimiento: Una entrada normal del usuario puede desencadenar un retroceso catastrófico, haciendo que los tiempos de respuesta aumenten de milisegundos a segundos. Esto crea una latencia impredecible que es difícil de depurar porque solo se manifiesta con patrones de entrada específicos.

Incidentes de producción: Una regex vulnerable bloquea el bucle de eventos en Node.js o consume recursos del grupo de hilos. A medida que se acumulan las peticiones, aumenta la memoria y el sistema deja de responder. En microservicios, una regex vulnerable provoca fallos en cascada en los servicios dependientes.

Dificultad de detección: Los patrones que funcionan bien en pruebas con entradas cortas se vuelven exponencialmente lentos con entradas más largas. La vulnerabilidad suele pasar desapercibida hasta la fase de producción, lo que requiere un despliegue de emergencia durante un incidente activo.

Ejemplos de códigos

❌ No conforme:

function validateEmail(email) {
    const regex = /^([a-zA-Z0-9_\-\.]+)+@([a-zA-Z0-9_\-\.]+)+\.([a-zA-Z]{2,5})$/;
    return regex.test(email);
}

function extractURLs(text) {
    const regex = /(https?:\/\/)?([\w\-])+\.(\w+)+([\w\-\.,@?^=%&:/~\+#]*)+/g;
    return text.match(regex);
}

Por qué es inseguro: Los cuantificadores anidados ([a-zA-Z0-9_\\-\\.]+)+ crean un retroceso exponencial. Para un correo electrónico como ¡aaaaaaaaaaaaaaaaaaaa!el motor regex intenta innumerables combinaciones antes de fallar. La expresión regular de la URL tiene varios cuantificadores anidados que agravan el problema, por lo que es trivialmente explotable con entradas como largas cadenas de caracteres válidos sin la estructura esperada.

✅ Conforme:

function validateEmail(email) {
    const regex = /^[a-zA-Z0-9_\-\.]+@[a-zA-Z0-9_\-\.]+\.[a-zA-Z]{2,5}$/;
    return regex.test(email);
}

function extractURLs(text) {
    const regex = /https?:\/\/[\w\-]+\.[\w\-]+(?:[\w\-\.,@?^=%&:/~\+#]*)?/g;
    return text.match(regex);
}

Por qué es seguro: La eliminación de cuantificadores anidados elimina el retroceso catastrófico. Los cuantificadores simples como [a-zA-Z0-9_\-\.]+ se ejecutan en tiempo lineal. El patrón URL utiliza grupos no capturadores con sufijo opcional (?:...)? en lugar de la repetición anidada, lo que garantiza un rendimiento predecible independientemente de la longitud o el contenido de la entrada.

Conclusión

El rendimiento de las expresiones regulares es una preocupación de seguridad, no sólo una optimización. Revise todos los patrones regex en busca de cuantificadores anidados, clases de caracteres solapadas en grupos de repetición y alternativas ambiguas. Pruebe los patrones regex con entradas patológicas (cadenas largas de caracteres válidos seguidas de finales no válidos) para identificar retrocesos catastróficos antes de la implantación. Cuando sea posible, sustituya las regex complejas por funciones de análisis sintáctico de cadenas que tengan características de rendimiento predecibles.

Preguntas frecuentes

¿Tiene alguna pregunta?

¿Qué patrones provocan retrocesos catastróficos?

Los culpables comunes incluyen cuantificadores anidados como (a+)+, (a*)*, o (a+)*b. Alternancia con patrones superpuestos como (a|a)* o (a|ab)*. Repetición con componentes opcionales como (a?)+. Cualquier patrón en el que el motor regex pueda coincidir con la misma subcadena de varias formas crea un espacio de búsqueda exponencial. Tenga cuidado con los cuantificadores (+, *, {n,m}) dentro de grupos cuantificados.

¿Cómo puedo comprobar si mi regex es vulnerable a ReDoS?

Utilice herramientas en línea como regex101.com, que muestran los pasos de ejecución y advierten de retrocesos catastróficos. Cree entradas de prueba con cadenas largas de caracteres válidos seguidas de caracteres que fuercen el retroceso. Para el patrón /^(a+)+b$/, prueba con "aaaaaaaaaaaaaaa!" (más de 30 a, sin b). Si la ejecución tarda más de milisegundos, la expresión regular es vulnerable. Implemente tiempos de espera en las operaciones regex de producción como defensa en profundidad.

¿Cuál es la diferencia entre retroceso catastrófico y lineal?

El retroceso lineal se produce cuando la expresión regular prueba alternativas en secuencia pero no reevalúa las opciones anteriores. El trabajo crece linealmente con el tamaño de la entrada. El backtracking catastrófico se produce cuando los cuantificadores anidados obligan al motor a probar exponencialmente muchas combinaciones. Para una entrada de longitud n, el tiempo de ejecución puede ser O(2^n) o peor. La diferencia oscila entre milisegundos y minutos para tamaños de entrada modestos.

¿Puedo utilizar lookaheads y lookbehinds de forma segura?

Lookaheads (?=...) and lookbehinds (?<=...) themselves don't cause catastrophic backtracking, but they can hide vulnerable patterns. A lookahead containing (a+)+ is still vulnerable. Use lookarounds for their intended purpose (assertions without consuming characters), not as a workaround for complex matching. Keep the patterns inside lookarounds simple and test them thoroughly.

¿Existen motores regex que eviten el retroceso catastrófico?

RE2 (utilizado por Google) garantiza la ejecución en tiempo lineal prohibiendo por completo el backtracking. No soporta todas las características (backreferences, lookarounds) pero previene ReDoS completamente. Para comprobaciones de seguridad críticas, considere el uso de enlaces RE2 o motores similares. Para JavaScript, no existe una alternativa integrada, por lo que el diseño de patrones y los tiempos de espera son sus principales defensas.

¿Debo añadir tiempos de espera a todas las operaciones regex?

Para entradas no fiables (datos proporcionados por el usuario, respuestas de API externas), sí. Establece tiempos de espera razonables como 100-500ms dependiendo de la complejidad esperada. En Node.js, no se puede aplicar directamente un tiempo de espera a regex.test(), pero se puede validar primero la longitud de la entrada o ejecutar regex en un subproceso de trabajo con tiempo de espera. Rechaza las entradas que excedan los límites razonables de longitud antes de intentar la concordancia regex.

¿Cómo puedo corregir un patrón regex vulnerable existente?

En primer lugar, determine si necesita regex. Muchas tareas de validación son más sencillas con métodos de cadena como includes(), startsWith() o split(). Si necesita regex, elimine los cuantificadores anidados aplanando el patrón. Sustituya (a+)+ por a+. Utilice grupos atómicos o cuantificadores posesivos si su motor regex los admite. Para patrones complejos, considere la posibilidad de analizar la entrada en varias pasadas con operaciones regex o de cadena más sencillas.

Asegúrese gratis

Proteja el código, la nube y el tiempo de ejecución en un sistema central.
Encuentre y corrija vulnerabilidades de forma rápida y automática.

No requiere tarjeta de crédito | Escanea resultados en 32segs.