Problemas de denegación de servicio
La preocupación más común con las expresiones regulares es un ataque de denegación de servicio a través de patrones patológicos que se vuelven exponenciales, ¡o incluso súper exponenciales! - y parece que tardan una eternidad en resolverse. Estos solo pueden aparecer en datos de entrada particulares, pero generalmente se puede crear uno en el que esto no importa.
Cuáles serán estos dependerán en cierta medida de cuán inteligente sea el compilador de expresiones regulares que está utilizando, porque algunos de estos pueden detectarse durante el tiempo de compilación. Los compiladores de expresiones regulares que implementan la recursividad generalmente tienen un contador de profundidad de recursión incorporado para verificar la no progresión.
El excelente artículo de 2007 de Russ Cox sobre la coincidencia de expresiones regulares puede ser simple y rápido (pero es lento en Java, Perl, PHP, Python, Ruby, ...) habla sobre las formas en que la mayoría de los NFA modernos, que parecen derivar del código de Henry Spencer , sufren una severa degradación del rendimiento, pero donde un NFA de estilo Thompson no tiene tales problemas.
Si solo admite patrones que los DFA pueden resolver, puede compilarlos como tales y se ejecutarán más rápido, posiblemente mucho más rápido. Sin embargo, lleva tiempo hacer esto. El documento de Cox menciona este enfoque y sus problemas relacionados. Todo se reduce a una clásica compensación tiempo-espacio.
Con un DFA, pasas más tiempo construyéndolo (y asignando más estados), mientras que con un NFA pasas más tiempo ejecutándolo, ya que pueden ser múltiples estados al mismo tiempo, y el retroceso puede comer tu almuerzo y tu CPU.
Soluciones de denegación de servicio
Probablemente, la forma más razonable de abordar estos patrones que están en el extremo perdedor de una carrera con la muerte por calor del universo es envolverlos con un temporizador que coloque efectivamente la cantidad máxima de tiempo permitido para su ejecución. Por lo general, esto será mucho, mucho menos que el tiempo de espera predeterminado que proporcionan la mayoría de los servidores HTTP.
Hay varias formas de implementar esto, desde un simple alarm(N)
nivel C hasta try {}
bloquear algunas excepciones de tipo alarma de captura, hasta generar un nuevo hilo especialmente creado con una restricción de tiempo incorporada.
Código de llamadas
En lenguajes de expresiones regulares que admiten llamadas de código, algún mecanismo para permitir o impedir éstos de la cadena que se va a compilar debe ser proporcionada. Incluso si las llamadas de código son solo para codificar en el idioma que está utilizando, debe restringirlas; no tienen que poder llamar a código externo, aunque si pueden, tienes problemas mucho mayores.
Por ejemplo, en Perl no se pueden tener códigos de llamadas en expresiones regulares creadas a partir de la interpolación de cadenas (como serían, ya que se compilan durante el tiempo de ejecución) a menos que el pragma especial de ámbito léxico esté use re "eval";
activo en el ámbito actual.
De esa manera, nadie puede colarse en una llamada de código para ejecutar programas del sistema como rm -rf *
, por ejemplo. Debido a que las llamadas de código son tan sensibles a la seguridad, Perl las deshabilita de manera predeterminada en todas las cadenas interpoladas, y tiene que hacer todo lo posible para volver a habilitarlas.
Definido por el usuario \ P {propiedades}
Sigue existiendo una cuestión más sensible a la seguridad relacionados con las propiedades de estilo Unicode - como \pM
, \p{Pd}
, \p{Pattern_Syntax}
, o \p{Script=Greek}
- que puede existir en algunos compiladores de expresiones regulares que el apoyo que la notación.
El problema es que en algunos de estos, el conjunto de propiedades posibles es extensible por el usuario. Eso significa que puede tener propiedades personalizadas que son llamadas de código reales a funciones con nombre en algún espacio de nombre en particular, como \p{GoodChars}
o \p{Class::Good_Characters}
. Merece la pena mirar cómo maneja su idioma.
Sandboxing
En Perl, un compartimento de espacio aislado a través del Safe
módulo daría control sobre la visibilidad del espacio de nombres. Otros idiomas ofrecen tecnologías de sandboxing similares. Si tales dispositivos están disponibles, es posible que desee examinarlos, porque están diseñados específicamente para la ejecución limitada de código no confiable.