SnakeYaml 2.0: cómo resolver la vulnerabilidad de deserialización insegura
21 de junio de 2023
0 minutos de lecturaEn diciembre del año pasado, te informamos sobre CVE-2022-1471. Este problema de deserialización insegura podía facilitar la ejecución de código arbitrario en las circunstancias adecuadas.
En la publicación detallada del blog “Vulnerabilidad de deserialización insegura en SnakeYaml (CVE-2022-1471)”, expliqué los problemas de esta biblioteca y cómo se podía explotar. En esencia, el problema consistía en que, de manera predeterminada, SnakeYaml analizaba el yaml entrante como un tipo de objeto genérico. Esto abre la posibilidad de deserializar otras clases disponibles en el classpath. Aunque se genere una ClassCastException, si el objeto ya se cargó, el daño está hecho.
El impacto depende en gran medida del uso que le des a la biblioteca. Por ejemplo, muchos desarrolladores solo usan yaml para proporcionar configuración a sus aplicaciones. Esta vulnerabilidad solo se puede explotar si aceptas yaml de fuentes desconocidas, como usuarios finales. Aun así, simplemente no podemos predecir cómo se usará una biblioteca como esta, y la configuración predeterminada debería ser segura.
Actualizar SnakeYaml con Snyk Open Source
Usemos Snyk Open Source para encontrar un reemplazo para la antigua biblioteca SnakeYaml. Si ejecuto localmente snyk test con Snyk CLI, veo que hay un reemplazo disponible.

La interfaz web también me indica que hay una versión 2.0 de SnakeYaml disponible para resolver el problema. El único inconveniente es que Spring Boot 3 todavía incluye la versión 1.x, así que tengo que reemplazar la versión manualmente en el archivo de manifiesto de Maven o Gradle.

Ten en cuenta que cambiar manualmente la biblioteca por una nueva versión principal puede provocar fallas. No hagas estos cambios sin más y considera el posible impacto en el funcionamiento interno de tu aplicación.
Cómo mitigar la deserialización con SnakeYaml 2.0
SnakeYaml 2.0 se lanzó a principios de 2023 para mitigar el comportamiento predeterminado que podía llevar a la ejecución de código arbitrario. En esta versión, el constructor que usa cada nuevo yaml() ahora extiende SafeConstructor. Como resultado, solo podemos analizar un conjunto limitado de tipos. SafeConstrutor de SnakeYaml puede construir clases estándar de Java, como tipos primitivos y clases básicas como string y map.
Los tipos específicos ya no se pueden analizar de forma predeterminada. Por eso, el siguiente archivo yaml generará una excepción:
Ahora, la implementación predeterminada ya no es vulnerable. Sin embargo, este es un cambio incompatible, así que es probable que tu código inicial deje de funcionar.
Cómo corregir la lógica de análisis de Yaml al usar SnakeYaml 2.x
En primer lugar, debemos asegurarnos de dejar de usar SnakeYaml 1.x. Incluso la versión más reciente de Spring Boot, la 3.1, todavía no incluye SnakeYaml 2.x. Esto significa que debemos actualizarlo nosotros mismos en el archivo de manifiesto. Para las implementaciones de Maven, podemos usar la sección <dependencyManagement> del archivo pom entre otras cosas. En Gradle, también es posible actualizar las dependencias transitivas con restricciones de dependencias.
Ten en cuenta que SnakeYaml 2.x introduce cambios incompatibles en la API con respecto a las versiones anteriores. Esto significa que debemos reescribir la implementación del análisis de yaml para usar los nuevos valores predeterminados seguros y hacer que vuelva a funcionar.
Veamos un dominio muy simple con dos clases:
Persona
Comentario
Person.java:
Comment.java:
Si quieres crear un archivo yaml a partir de una entidad como la anterior, probablemente hayas escrito algo parecido a lo siguiente al usar SnakeYaml 1.x:
Esto genera el siguiente archivo yaml:
Con SnakeYaml 2.x, !!mypackage.Person ya no se acepta. Ahora podemos eliminar la referencia al objeto al analizarlo para convertirlo en un archivo yaml. Sin embargo, sigue habiendo un problema con los archivos yaml exportados antes de esta migración.
Por suerte, ¡todavía podemos resolver este problema! Al analizar un objeto específico, puedes configurar el constructor que debe usar el analizador. Además, podemos agregar un TagInspector específico a LoaderOptions que permita la etiqueta de nuestro paquete. Así, solo se permiten archivos yaml que coincidan con tu objeto y sean compatibles con versiones anteriores de los archivos yaml creados con las versiones 1.x.
Además, sería mejor eliminar por completo de tu archivo yaml la referencia o etiqueta al objeto real. Esto ya era posible en versiones anteriores de SnakeYaml: bastaba con agregar un representer al objeto yaml para asignar la etiqueta del objeto de nivel superior a map. A continuación, verás un ejemplo compatible con SnakeYaml 2.x:
El archivo yaml ya no comenzará con !!mypackage.Person (ni algo similar). Si todos tus archivos yaml están limpios, puedes quitar TagInspector del analizador.
Mantente al día con Snyk
Mantenerte al día con todas las versiones de tus bibliotecas es fundamental para la seguridad del código abierto. Usar SnakeYaml 1.x puede generar problemas de seguridad innecesarios si aceptas archivos yaml de fuentes externas, directa o indirectamente.
Snyk Open Source puede ayudarte a encontrar y corregir estos problemas, o recomendarte una versión alternativa si es necesario. En el siguiente ejemplo, mostramos que actualizar al menos a la versión 2.0 de SnakeYaml elimina la vulnerabilidad de ejecución de código arbitrario.

Además, si conectas tu repositorio de git a Snyk, podemos ofrecerte pull requests para mantener tus dependencias al día, incluso si no hay un problema de seguridad crítico. Más vale prevenir que lamentar, y mantener tus bibliotecas actualizadas te ayuda a reducir el riesgo de vulnerabilidades y el trabajo adicional que implica actualizar debido a un problema de seguridad.

Empieza con los desafíos de Capture the Flag
Aprende a resolver desafíos de captura la bandera con nuestro taller virtual introductorio a pedido.


