La decadencia de las expresiones regulares
«Cuando surge un problema, algunas personas deciden que las expresiones regulares son la solución, momento en el que pasan a tener dos problemas». – Jamie Zawinski</u>
Las expresiones regulares son una forma concisa de identificar patrones o secuencias de tokens, generalmente en formato de texto. La pregunta es si comparar cadenas de texto resulta efectivo para detectar malware o ataques a aplicaciones web y, si hilamos más fino, para qué tipos de ataques conviene utilizar expresiones regulares. Al fin y al cabo, no podemos arriesgarnos a mantener el statu quo y seguir utilizando métodos ineficaces que pueden poner en peligro la seguridad.
Las expresiones regulares resultan útiles cuando los atacantes son inexpertos y no saben ni cómo funcionan sus propios ataques, porque alguien los ha creado para ellos. Básicamente, se limitan a copiar y pegar lo que les dan y a hacer de las suyas en internet utilizando Shodan o una herramienta similar. Sin embargo, las expresiones regulares no sirven para cosas más complejas.
Quienes crean los fragmentos reutilizables de un ataque saben que pueden adaptarlos para eludir un mecanismo tan sencillo como la comprobación de coincidencia de patrones en la que se basan las expresiones regulares. De hecho, la secuencia de los tokens prácticamente carece de importancia, por lo que debemos dirigir nuestra atención a la expresión de esos tokens en su totalidad.
El quid de la cuestión es el comportamiento de los ataques; es decir, la forma en que se desarrollan en el espacio y el tiempo. Más que una descripción de un ataque, los fragmentos de texto ofrecen detalles acerca de su implementación.
A modo de analogía, imaginemos que alguien utiliza un destornillador para retirar un televisor de la pared. ¿Es necesario saber si es plano o de estrella? Igual somos nosotros quienes lo hemos utilizado para ajustar la altura del televisor, no un ladrón con la intención de llevárselo. No obstante, si entramos en el salón y nos encontramos con una ventana rota y una pared sin tele, está claro que alguien ha actuado con premeditación y alevosía.
Lo que queremos decir con esto es que un patrón de texto tiene un significado distinto y produce unos resultados u otros dependiendo del entorno. El contexto lo es todo. No es lo mismo aprovecharse de la descripción textual de una inyección de código SQL (SQLi) en la entrada de un blog que lanzar un ataque por SQLi dirigido a quien aloja dicho blog. El patrón de texto que representa el tipo de ataque está presente en ambos casos, pero solo uno de ellos se puede considerar un ataque como tal.
Esto no significa que las expresiones regulares carezcan de utilidad en todos los casos; ofrecen representaciones en formato reducido para la comprobación de coincidencia de patrones, son idiomáticas y se pueden leer con cierta facilidad. El problema reside en la comprobación de coincidencia de patrones, ya que los tokens encargados de ella, como el texto de los parámetros de consulta y los campos POST del cuerpo, solo detectan los ataques más rudimentarios. Lo mismo ocurre fuera del ámbito de las aplicaciones web: la expresión regular del hash de un archivo concreto da por hecho que los atacantes son tan descuidados y están tan desmotivados que ni se molestan en convertir el archivo para eludir un mecanismo de detección sumamente simple. Puede que sea así, pero no ocurre en la mayoría de los casos.
Más allá de la precisión, la realidad es que las expresiones regulares pueden obstaculizar la protección. Muchas de ellas son inflexibles, poco manejables y difíciles de mantener, sobre todo a la hora de añadir más contexto. Por ejemplo, la captura de una lista de comandos válidos de Linux o Windows para detectar la inyección de comandos sería ilegible o directamente imposible de hacer en condiciones mediante expresiones regulares, y lo mismo se puede decir de la SQLi. Un humano no podría leer el patrón resultante, y eso es insostenible por mucho que se creen gramáticas de expresiones regulares</u>.
Fíjate en esta expresión regular para validar direcciones de correo electrónico</u>:
`(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])`
Sencilla, ¿verdad? Esperamos que no te hayas dejado los ojos intentando descifrar este jeroglífico.
Fuente de la imagen
Seguir el ritmo a las técnicas y los patrones de ataque, ya sean nuevos o cambiantes, requiere tiempo, esfuerzo y mucha cafeína, razón por la cual la solución se acaba actualizando cada vez menos y pierde eficacia. Trabajar con expresiones regulares es un proceso lento al que se le deben dedicar muchos recursos, sobre todo si son complejas y, por tanto, difíciles de optimizar. La inversión en términos de trabajo y rendimiento que exige la detección de ataques mediante expresiones regulares no se suele amortizar.
Las expresiones regulares tienen sus cosas buenas, pero la detección de ataques no es una de ellas. Muchos proveedores no quieren que sus clientes se enteren, porque mantener la tecnología existente sale mucho más barato que innovar, pero el problema es que las expresiones regulares no intimidan a los atacantes ni suponen un mayor coste para ellos.
Algunas técnicas, como el análisis de peticiones HTTP, son una inversión más segura, ya que permiten a los equipos de seguridad añadir más contexto para aplicar la comprobación de coincidencia de patrones a los comportamientos maliciosos y, ya de paso, reducir la carga de trabajo de los humanos y las máquinas. Al diseccionar estos comportamientos, es posible detectar ataques rudimentarios y otros más avanzados, así como sus variantes, sin necesidad de establecer una nueva regla cada vez que aparezca una nueva vulnerabilidad, lo cual es bastante frecuente. Si nos fijamos en el contexto de un ataque y la forma en que se procesa una petición en tiempo de ejecución, podemos tomar decisiones mejor fundadas que con las expresiones regulares.
Y, con esto, llegamos a la paradoja que existe en el mercado actual: las listas de requisitos de quienes contratan soluciones de seguridad para aplicaciones web (y también otros productos, como las reglas de YARA en herramientas de puntos de conexión) todavía incluyen la comprobación de coincidencia de patrones de expresiones regulares, a pesar de su ineficacia. En su momento tenía sentido, sobre todo cuando la mayoría de los productos de los proveedores utilizaban expresiones regulares y no había nada mejor, pero ahora vivimos en otro mundo.
Better security without regex. Learn how Fastly signals give you better visibility for decisioning.
Toma las riendas y actualiza tus prioridades
Veamos una situación a modo de ejemplo. Un cliente entra en una tienda de motos y pregunta si un modelo viene con estribos.
—No —responde la vendedora con cara de confusión—. Una moto no necesita estribos. El cliente tuerce el morro y le enseña su lista de requisitos.
—¿Y una fusta? —le pregunta.
—Tampoco. No hace falta fustigar a una moto —responde la vendedora.
—¡Ahora me dirá que tampoco viene con riendas y bridas! —espeta el cliente.
—En efecto. Es una moto —puntualiza la vendedora mientras se arma de paciencia—. De todas formas, ¿qué necesita exactamente?
—Algo que me lleve del punto A al punto B lo más rápido posible —explica el cliente con total convicción.
—Aquí vendemos motos, no caballos, y están diseñadas justo para lo que usted necesita. Sus innovaciones le harán la vida más fácil —explica la vendedora.
—¡Usted dirá lo que quiera, pero a mí me faltan muchas cosas! —concluye el cliente.
Este diálogo es totalmente absurdo, pero guarda cierto parecido con algunas de las conversaciones que se dan en el mundo de la ciberseguridad. Cuando vamos a comprar algo, es perfectamente comprensible que nos ciñamos a una lista de cosas que ya conocemos. Preguntar por estribos, riendas y bridas tiene todo el sentido del mundo si buscamos un caballo. No obstante, estas listas invariables por naturaleza pueden llevarnos por el camino de la obsolescencia.
Nos quejamos mucho de que los atacantes sean cada vez más rápidos y certeros, pero a veces elegimos productos con tecnologías ancladas en el pasado. Los atacantes manejan multitud de opciones y pueden elegir las herramientas adecuadas para alcanzar sus objetivos, así que nosotros debemos hacer lo mismo como responsables de la seguridad. Conviene tener en cuenta técnicas más modernas y de eficacia probada, como el análisis de los parámetros de las peticiones y la observación de los resultados en tiempo de ejecución, para disfrutar de más velocidad, precisión y flexibilidad.
No es fácil asumir que nuestras soluciones a un problema son incorrectas o están anticuadas y que la realidad ha evolucionado más rápido que nuestra percepción de ella, sobre todo después de apostar una cantidad ingente de recursos a un caballo perdedor, pero esa reticencia a cambiar y replantearse las cosas es nuestro punto más débil. Si queremos ganar la carrera a los atacantes, debemos dejar atrás las expresiones regulares y adentrarnos en la nueva era de la detección.
Descubre cómo las herramientas de análisis de Fastly ayudan a las empresas a dejar atrás las expresiones regulares. Puedes consultar esta hoja de datos sobre la detección con SmartParse</u>, leer esta entrada del blog sobre la contención del ataque Log4Shell</u> o solicitarnos una demostración</u>. Será un placer explicarte cómo funcionan.