Articulo de referencia

ReDoS

Un ataque de denegación de servicio por expresiones regulares ( ReDoS ) [ 1 ] es un ataque de complejidad algorítmica que produce una denegación de servicio al proporcionar una ...

Un ataque de denegación de servicio por expresiones regulares ( ReDoS ) [ 1 ] es un ataque de complejidad algorítmica que produce una denegación de servicio al proporcionar una expresión regular (regex) y/o una entrada que requiere mucho tiempo para evaluarse. El ataque explota el hecho de que muchas [ 2 ] implementaciones de expresiones regulares tienen una complejidad superlineal en el peor de los casos ; en ciertos pares de regex-entrada, el tiempo empleado puede crecer de forma polinómica o exponencial en relación con el tamaño de la entrada. Un atacante puede, por lo tanto, hacer que un programa consuma un tiempo considerable al proporcionar una expresión regular y/o entrada especialmente diseñadas. El programa entonces se ralentizará o dejará de responder. [ 3 ] [ 4 ]

Descripción

La coincidencia de expresiones regulares ("regex") se puede realizar mediante la construcción de un autómata de estados finitos . Las expresiones regulares se pueden convertir fácilmente en autómatas no deterministas (ANF), en los que, para cada estado y símbolo de entrada, puede haber varios estados siguientes posibles. Después de construir el autómata, existen varias posibilidades:

  • El motor puede convertirlo en un autómata finito determinista (AFD) y procesar la entrada a través del resultado;
  • El motor puede intentar todas las rutas posibles una por una hasta que se encuentre una coincidencia o hasta que todas las rutas se hayan intentado y fallen (" retroceso "). [ 5 ] [ 6 ]
  • El motor puede considerar en paralelo todas las rutas posibles a través del autómata no determinista;
  • El motor puede convertir el autómata no determinista en un DFA de forma perezosa ( es decir , sobre la marcha, durante la coincidencia).

De los algoritmos anteriores, los dos primeros son problemáticos. El primero es problemático porque un autómata determinista podría tener hasta2metro{\displaystyle 2^{m}}estados dondemetro{\displaystyle m}es el número de estados en el autómata no determinista; por lo tanto, la conversión de NFA a DFA puede tomar un tiempo exponencial . El segundo es problemático porque un autómata no determinista podría tener un número exponencial de caminos de longitudnorte{\displaystyle n}, de modo que caminar a través de una entrada de longitudnorte{\displaystyle n}también tomará un tiempo exponencial. [ 7 ] Sin embargo, los dos últimos algoritmos no exhiben un comportamiento patológico.

Tenga en cuenta que para expresiones regulares no patológicas, los algoritmos problemáticos suelen ser rápidos y, en la práctica, se puede esperar que " compilen " una expresión regular enO(metro){\displaystyle O(m)}tiempo y coincidir enO(norte){\displaystyle O(n)}tiempo; en cambio, la simulación de un NFA y el cálculo perezoso del DFA tienenO(metro2norte){\displaystyle O(m\cdot 2^{n})}Complejidad del peor caso. [ a ] ​​La denegación de servicio de expresiones regulares ocurre cuando estas expectativas se aplican a una expresión regular proporcionada por el usuario, y las expresiones regulares maliciosas proporcionadas por el usuario activan la complejidad del peor caso del comparador de expresiones regulares.

Si bien los algoritmos de expresiones regulares pueden escribirse de manera eficiente, la mayoría de los motores de expresiones regulares existentes extienden los lenguajes de expresiones regulares con construcciones adicionales que no siempre se pueden resolver de manera eficiente. Estos patrones extendidos obligan, en esencia, a la implementación de expresiones regulares en la mayoría de los lenguajes de programación a utilizar retroceso.

Ejemplos

Retroceso exponencial

El tipo de problema más grave ocurre con las coincidencias de expresiones regulares de retroceso, donde algunos patrones tienen un tiempo de ejecución que es exponencial en la longitud de la cadena de entrada. [ 8 ] Para cadenas denorte{\displaystyle n}personajes, el tiempo de ejecución esO(2norte){\displaystyle O(2^{n})}Esto ocurre cuando una expresión regular tiene tres propiedades:

  • La expresión regular aplica repetición ( +, *) a una subexpresión;
  • La subexpresión puede coincidir con la misma entrada de varias maneras, o bien puede coincidir con una cadena de entrada que sea un prefijo de una coincidencia posible más larga;
  • y después de la subexpresión repetida, hay una expresión que coincide con algo con lo que la subexpresión no coincide.

La segunda condición se explica mejor con dos ejemplos:

  • En (a|a)+$, la repetición se aplica a la subexpresión a|a, que puede coincidir ade dos maneras en cada lado de la alternancia.
  • En (a+)*$, la repetición se aplica a la subexpresión a+, que puede coincidir con ao aa, etc.

En ambos ejemplos usamos $para hacer coincidir el final de la cadena, satisfaciendo la tercera condición, pero también es posible usar otro carácter para esto. Por ejemplo (a|aa)*ctiene la misma estructura problemática.

Las tres expresiones regulares anteriores mostrarán un tiempo de ejecución exponencial cuando se apliquen a cadenas de la formaa...aincógnita{\displaystyle a...ax}. Por ejemplo, al intentar compararlos con aaaaaaaaaaaaaaaaaaaaaaaaxun motor de expresiones de retroceso, tardará un tiempo significativamente largo en completarse, y el tiempo de ejecución se duplicará aproximadamente por cada adicional aantes del x.

También es posible tener retroceso, que es de tiempo polinomial.O(norteincógnita){\displaystyle O(n^{x})}, en lugar de exponencial. Esto también puede causar problemas para entradas suficientemente largas, aunque se le ha prestado menos atención a este problema ya que una entrada maliciosa debe ser mucho más larga para tener un efecto significativo. Un ejemplo de tal patrón es " a*b?a*c", cuando la entrada es una secuencia arbitrariamente larga de " a"s.

Expresiones regulares vulnerables en repositorios en línea

Se han encontrado expresiones regulares denominadas "malvadas" o vulnerables en repositorios de expresiones regulares en línea. Cabe destacar que basta con encontrar una subexpresión vulnerable para atacar la expresión regular completa:

  1. RegExLib, id=1757 (validación de correo electrónico) – ver parte roja^([a-zA-Z0-9])(([\-.]|[_]+)?([a-zA-Z0-9]+))*(@){1}[a-z0-9]+[.]{1}(([a-z]{2,3})|([a-z]{2,3}[.]{1}[a-z]{2,3}))$
  2. Repositorio de expresiones regulares de validación de OWASP , nombre de clase Java: ver la parte roja.^(([a-z])+.)+[A-Z]([a-z])+$

Estos dos ejemplos también son susceptibles a la entrada aaaaaaaaaaaaaaaaaaaaaaaa!.

Ataques

Si la expresión regular se ve afectada por la entrada del usuario, como en un servicio web que permite a los clientes introducir un patrón de búsqueda, un atacante puede inyectar una expresión regular maliciosa para consumir los recursos del servidor. Por lo tanto, en la mayoría de los casos, la denegación de servicio por expresiones regulares se puede evitar eliminando la posibilidad de que el usuario ejecute patrones arbitrarios en el servidor. En este caso, las aplicaciones web y las bases de datos son las principales aplicaciones vulnerables. Alternativamente, una página maliciosa podría bloquear el navegador del usuario o provocar un consumo excesivo de memoria.

Sin embargo, si ya existe una expresión regular vulnerable en el servidor, un atacante podría proporcionar una entrada que desencadene su comportamiento más crítico. En este caso, los escáneres de correo electrónico y los sistemas de detección de intrusiones también podrían ser vulnerables.

En el caso de una aplicación web, el programador puede usar la misma expresión regular para validar la entrada tanto en el lado del cliente como en el del servidor. Un atacante podría inspeccionar el código del cliente, buscando expresiones regulares maliciosas, y enviar datos manipulados directamente al servidor web para bloquearlo. [ 9 ]

Mitigación

ReDoS puede mitigarse sin cambios en el motor de expresiones regulares, simplemente estableciendo un límite de tiempo para la ejecución de expresiones regulares cuando se trata de entradas no confiables. [ 10 ]

Los ataques ReDoS pueden evitarse por completo utilizando una implementación de expresiones regulares no vulnerable. Después de que el firewall de aplicaciones web (WAF) de CloudFlare se cayera debido a un ataque ReDoS de PCRE en 2019, la empresa reescribió su WAF para usar la biblioteca de expresiones regulares Rust sin retroceso, utilizando un algoritmo similar a RE2 . [ 11 ] [ 12 ]

Las expresiones regulares vulnerables pueden detectarse programáticamente mediante un linter . [ 13 ] Los métodos abarcan desde el análisis estático puro [ 14 ] [ 15 ] hasta el fuzzing . [ 16 ] En la mayoría de los casos, las expresiones regulares problemáticas pueden reescribirse como patrones "no maliciosos". Por ejemplo, (.*a)+puede reescribirse como ([^a]*a)+. La coincidencia posesiva y la agrupación atómica , que desactivan el retroceso para partes de la expresión, [ 17 ] también pueden utilizarse para "pacificar" las partes vulnerables. [ 18 ] [ 19 ]

Implementación en tiempo lineal (autómatas finitos)

Si bien algunas bibliotecas de expresiones regulares no cuentan con defensa integrada contra ataques ReDoS, como la biblioteca estándar de C++<regex> , la biblioteca C POSIX <regex.h>[ 20 ] o Boostboost.regex (que utilizan retroceso, lo que conlleva un tiempo exponencial), otras bibliotecas de expresiones regulares están diseñadas para prevenir ataques de denegación de servicio basados ​​en expresiones regulares. Esto se logra mediante autómatas finitos deterministas, que se ejecutan en tiempo lineal en relación con el tamaño de la entrada.

Utilizando la biblioteca RE2 de Google para C++ : [ 21 ]

import < re2 / re2 . h > ; import std ;using std :: string_view ; using re2 :: RE2 ;constexpr string_view TEXT = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!" ; constexpr string_view PATTERN = "^(a+)+$" ;int main ( int argc , char * argv []) { bool isMatch = RE2 :: FullMatch ( TEXT , PATTERN ); std :: println ( "Resultado de la coincidencia: {}" , isMatch ); }

Uso de la regexbiblioteca para Rust : [ 22 ]

usar regex :: Regex ;const TEXTO : & str = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!" ; PATRÓN constante : & str = r"^(a+)+$" ;fn main () { // Regex::new() devuelve Result<Regex, Error> y debe ser unwrapped match Regex :: new ( PATTERN ) { Ok ( re ) => { let is_match : bool = re . is_match ( TEXT ); println! ( "Resultado de la coincidencia: {}" , is_match ); } Err ( err ) => { eprintln! ( "Error al desempaquetar la expresión regular: {}" , err ); } } }

Tiempo de espera de coincidencia de expresiones regulares

Los tiempos de espera pueden utilizarse para cancelar tareas de expresiones regulares si tardan demasiado.

Los tiempos de espera están integrados en la biblioteca estándar de .NET , ya que la clase System.Text.RegularExpressions.Regexadmite una propiedad MatchTimeout. [ 23 ] El siguiente es un ejemplo en C# :

espacio de nombres Wikipedia.Ejemplos ;using System ; using System.Text.RegularExpressions ;public class Example { private const string Text = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!" ; private const string Pattern = @"^(a+)+$" ;static void Main ( string [] args ) { try { Regex re = new ( Pattern , RegexOptions.None , TimeSpan.FromMilliseconds ( 100 ) ); bool isMatch = re.IsMatch ( Text ); Console.WriteLine ( $"Resultado de la coincidencia: {isMatch}" ) ; } catch ( RegexMatchTimeoutException ex ) { Console.WriteLine ( $ " ¡ La operación de Regex ha expirado! {ex.Message}" ) ; } } }

Expresiones regulares en tiempo de compilación

Las expresiones regulares de compilación trasladan el análisis sintáctico y la validación al tiempo de compilación , eliminando la sobrecarga del tiempo de ejecución . Sin embargo, las expresiones regulares de compilación solo son útiles cuando el patrón se puede proporcionar en tiempo de compilación.

La biblioteca de expresiones regulares en tiempo de compilación de C++ ( ctre ), de Hana Dusíková, proporciona un ctre::match<>functor que realiza el análisis de expresiones regulares en tiempo de compilación. [ 24 ] Si un patrón en tiempo de compilación provoca que la compilación supere el constexprlímite de pasos de evaluación, la compilación fallará. Se ha propuesto para la biblioteca estándar de C++ , pero aún no se ha incluido. [ 25 ]

import < ctre.hpp > ; import std ;usando std :: string_view ;constexpr string_view TEXT = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!" ;int main ( int argc , char * argv []) { // ctre::match<> es un tipo ctre::regex_results<> // la lógica de coincidencia se realiza en tiempo de compilación cuando se marca constexpr constexpr bool isMatch = ctre :: match < "^(a+)+$" > ( TEXT ); std :: println ( "Resultado de la coincidencia: {}" , isMatch ); }

El lenguaje de programación D ofrece std.regex.ctRegex, que es una expresión regular en tiempo de compilación. [ 26 ]

En Rust, las expresiones regulares en tiempo de compilación se pueden lograr mediante algunas bibliotecas de terceros, como lazy_regex , ctreg y regex-automata , que ofrecen varias macros para la validación de expresiones regulares en tiempo de compilación. [ 27 ]

En C#, existe un System.Text.RegularExpressions.GeneratedRegexAttributeatributo que genera una implementación de una expresión regular mediante la generación de código fuente en un partialmétodo. [ 28 ]

espacio de nombres Wikipedia.Ejemplos ;using System ; using System.Text.RegularExpressions ;public partial class Example { [GeneratedRegex(@"^(a+)+$")] private static partial Regex Pattern ();const privada string Texto = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa!" ;static void Main ( string [] args ) { // La lógica de IsMatch() se ejecuta en tiempo de ejecución bool isMatch = Pattern (). IsMatch ( Text ); Console.WriteLine ( $"Resultado de la coincidencia: {isMatch}" ); } }

Véase también

Referencias

  1. El cálculo perezoso del autómata finito determinista (AFD) suele alcanzar la velocidad de los autómatas deterministas, manteniendo un comportamiento en el peor de los casos similar al de la simulación de un autómata finito no determinista (AFND). Sin embargo, su implementación es considerablemente más compleja y puede consumir más memoria.
  1. OWASP (10 de febrero de 2010). "Regex Denial of Service" . Consultado el 16 de abril de 2010 .
  2. Davis, James; Louis, Michael; Coghlan, Christy; Servant, Francisco; Lee, Dongyoon (2019). "¿Por qué las expresiones regulares no son una lengua franca? Un estudio empírico sobre la reutilización y la portabilidad de las expresiones regulares" (PDF) . Conferencia Conjunta Europea de Ingeniería de Software de la ACM y Simposio sobre los Fundamentos de la Ingeniería de Software : 443–454 .
  3. RiverStar Software (18 de enero de 2010). "Boletín de seguridad: Precaución al usar expresiones regulares" . Archivado del original el 15 de julio de 2011. Consultado el 16 de abril de 2010 .
  4. Ristic, Ivan (15 de marzo de 2010). Manual de ModSecurity . Londres, Reino Unido: Feisty Duck Ltd. pág. 173. ISBN  978-1-907117-02-2Archivado del original el 8 de agosto de 2016. Consultado el 16 de abril de 2010 .
  5. Crosby y Wallach, Usenix Security (2003). "Regular Expression Denial Of Service" . Archivado del original el 1 de marzo de 2005. Consultado el 13 de enero de 2010 .
  6. Bryan Sullivan (3 de mayo de 2010). "Ataques y defensas de denegación de servicio mediante expresiones regulares" . Consultado el 6 de mayo de 2010 .
  7. Kirrage, J.; Rathnayake, A.; Thielecke, H. (2013). "Análisis estático para ataques de denegación de servicio mediante expresiones regulares". Seguridad de redes y sistemas . Madrid, España: Springer. pp. 135–148 . arXiv : 1301.0849 . doi : 10.1007/978-3-642-38631-2_11 . 
  8. Jim Manico y Adar Weidman (07-12-2009). "Podcast OWASP 56 (ReDoS)" . Recuperado el 02-04-2010 .
  9. Barlas, Efe; Du, Xin; Davis, James (2022). "Explotación de la sanitización de entradas para la denegación de servicio mediante expresiones regulares" (PDF) . Conferencia internacional ACM/IEEE sobre ingeniería de software : 1–14 . arXiv : 2303.01996 .
  10. "Retroceso en expresiones regulares de .NET - .NET" . learn.microsoft.com . 11 de agosto de 2023. Al usar System.Text.RegularExpressions para procesar entradas no confiables, se debe establecer un tiempo de espera. Un usuario malintencionado puede proporcionar entradas a RegularExpressions, lo que provoca un ataque de denegación de servicio. Las API del framework ASP.NET Core que usan RegularExpressions establecen un tiempo de espera.
  11. "Acelerando el WAF un 40%" . El blog de Cloudflare . 1 de julio de 2020.
  12. Cox, Russ (2007). "La coincidencia de expresiones regulares puede ser simple y rápida" . Recuperado el 20 de abril de 2011 . describe el algoritmo RE2
  13. Véase, por ejemplo, Schmidt, Michael (30 de marzo de 2023). "RunDevelopment/scslre" . GitHub ., TSUYUSATO, Kitsune. "Revisar la Introducción" .y Davis, James. "vuln-regex-detector/src/detect/README.md" . GitHub .
  14. H. Thielecke, A. Rathnayake (2013). " Análisis estático de denegación de servicio (ReDoS) mediante expresiones regulares. Archivado el 3 de agosto de 2014 en Wayback Machine ". Recuperado el 30 de mayo de 2013.
  15. ^ B. van der Merwe, N Weideman (2017). " Análisis estático de expresiones regulares ". Consultado el 12 de agosto de 2017.
  16. "Fuzzing con análisis estático | recheck" . makenowjust-labs.github.io .
  17. "Clases esenciales: Expresiones regulares: Cuantificadores: Diferencias entre cuantificadores voraces, reticentes y posesivos" . Los tutoriales de Java . Oracle . Archivado del original el 7 de octubre de 2020. Consultado el 23 de diciembre de 2016 .
  18. "compose-regexp.js", "Coincidencia atómica"" . GitHub . 2 de enero de 2024."tc39/proposal-regexp-atomic-operators" . Ecma TC39. 31 de diciembre de 2023.
  19. "Prevención de ataques de denegación de servicio (ReDoS) mediante expresiones regulares" . www.regular-expressions.info
  20. "regex(3) - Página del manual de Linux" . man7.org . 28 de junio de 2025.
  21. "Github - google/re2" . github.com . 1 de julio de 2025.
  22. "regex - Rust" . docs.rs. Consultado el 15 de septiembre de 2025 .
  23. "Propiedad Regex.MatchTimeout (System.Text.RegularExpressions) Microsoft Learn" . learn.microsoft.com . Consultado el 15 de septiembre de 2025 .
  24. Hana Dusíková (8 de mayo de 2026). "Expresiones regulares en tiempo de compilación v3" . github.com . Hana Dusíková.
  25. Hana Dusíková. "Expresiones regulares en tiempo de compilación" (PDF) . open-std.org . GT 21 . Consultado el 9 de julio de 2026 .
  26. Lenguaje D (9 de julio de 2026). "std.regex" . dlang.org . Lenguaje D.
  27. Canop (11 de febrero de 2026). "Crate lazy_regex" . docs.rs. docs.rs.
  28. Microsoft Learn. "GeneratedRegexAttribute Class" . learn.microsoft.com . Microsoft Learn . Consultado el 9 de julio de 2026 .
  • Ejemplos de ataques ReDoS en aplicaciones de código abierto:
    • ReDoS en DataVault (CVE-2009-3277)
    • ReDoS en EntLib (CVE-2009-3275)
    • ReDoS en NASD CORE.NET Terelik (CVE-2009-3276)
  • Algunos puntos de referencia para ReDoS
    • Achim Hoffman (2010). " ReDoS - benchmark para DoS de expresiones regulares en JavaScript ". Recuperado el 19 de abril de 2010.
    • Richard M. Smith (2010). " Resultados de la prueba de ataque de denegación de servicio (ReDoS) mediante expresiones regulares. Archivado el 22/07/2011 en Wayback Machine ". Consultado el 19/04/2010.