¿Hay alguna forma de poner código malicioso en una expresión regular?

138

Quiero agregar la capacidad de búsqueda de expresiones regulares a mi página web pública. Además de la codificación HTML de la salida, ¿debo hacer algo para protegerme de las entradas maliciosas de los usuarios?

Las búsquedas en Google están inundadas por personas que resuelven el problema inverso, usando expresiones regulares para detectar entradas maliciosas, lo que no me interesa. En mi escenario, la entrada del usuario es una expresión regular.

Voy a usar la expresión regular de la biblioteca en .NET (C #).

MatthewMartin
fuente
44
Esto podría depender del idioma y / o biblioteca de expresiones regulares que use.
Aschepler
Un poco más de material de lectura: ReDoS en OWASP , ReDoS en Wikipedia
joeytwiddle

Respuestas:

216

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 Safemó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.

tchrist
fuente
44
La conversión de NFA-> DFA puede producir una explosión de estado exponencial, convirtiendo un DoS de tiempo en un DoS de espacio, así como el costo de tiempo de generar el número exponencial de estados.
Barry Kelly
pero probablemente no necesitará todas las capacidades de expresiones regulares, ¿qué piensa sobre restringir el poder de las expresiones regulares como lo hizo google: google.com/intl/en/help/faq_codesearch.html#regexp
systemsfault
1
@Barry Muy bien. Había estado pensando en la estrategia de Russ Cox descrita en uno de sus documentos de compilación incremental de partes de la NFA en una DFA equivalente pero tirarla a la basura si se hacía demasiado grande. Pero no hay una bala de plata en un DFA, incluso si Thompson demostró que es equivalente a un NFA, porque sí tiene que pagar el gaitero en algún momento u otro. El tiempo dedicado a pedirle al sistema operativo más espacio, y los costos de configuración de la tabla de páginas que lo acompañan, a veces pueden empujar la escala de equilibrio hacia el otro lado y hacer que la conversión del tiempo al espacio sea menos atractiva de lo que sería.
tchrist
20

Además de la excelente respuesta de tchrist: ¡el mismo Russ Cox que escribió la página "Expresión regular" también ha publicado un código! re2 es una biblioteca de C ++ que garantiza el tiempo de ejecución de O (length_of_regex) y el límite de uso de memoria configurable. Se usa dentro de Google para que pueda escribir una expresión regular en la búsqueda de código de Google, lo que significa que ha sido probada en batalla.

Brian Bloniarz
fuente
2
De hecho así. Puede intercambiar re2 en el motor de expresiones regulares de Perl con un módulo, y usará re2 si es posible y Perl si no. Funciona bastante bien
tchrist
6

Querrás leer este artículo:

Cambio de contexto inseguro: inoculación de expresiones regulares para la supervivencia El documento trata más sobre lo que puede salir mal con los motores de expresión regular (por ejemplo, PCRE), pero puede ayudarlo a comprender a qué se enfrenta.

Bruce Ediger
fuente
1
Aquí hay un aviso de seguridad sobre el código GNU libc regcomp (3): securityreason.com/achievement_securityalert/93 ¡ Qué oportuno! Al menos en Linux, la vulnerabilidad es fácil de demostrar: grep -E ". * {10,} {10,} {10,} {10,} {10,}"
Bruce Ediger
5

No solo debe preocuparse por la coincidencia en sí misma, sino también por cómo hacerla. Por ejemplo, si su entrada pasa por algún tipo de fase de evaluación o sustitución de comando en su camino hacia el motor de expresión regular, podría haber código que se ejecute dentro del patrón. O, si su sintaxis de expresión regular permite comandos incrustados, también debe tener cuidado con eso. Como no especificó el idioma en su pregunta, es difícil decir con certeza cuáles son todas las implicaciones de seguridad.

Bryan Oakley
fuente
1

Una buena forma de probar sus RegEx's por problemas de seguridad (al menos para Windows) es la herramienta de fuzzing SDL RegEx lanzada recientemente por Microsoft. Esto puede ayudar a evitar una construcción RegEx patológicamente mala.

RandomNickName42
fuente