Problemas de deserialización JSON de Java con Jackson ObjectMapper
1 de diciembre de 2021
0 minutos de lecturaEn una publicación anterior del blog, analizamos la plataforma de serialización personalizada de Java y sus implicaciones de seguridad. Más recientemente, escribí sobre cómo las mejoras de Java 17 pueden ayudarte a prevenir la deserialización insegura. Sin embargo, hoy en día, las personas ya no dependen tanto de la serialización personalizada de Java, sino que optan por usar JSON. JSON es el formato de serialización de datos más difundido, es legible para las personas y no es específico de Java.
Una de las bibliotecas más utilizadas es jackson-databind, que te ofrece un ObjectMapper para transformar tus objetos en JSON y viceversa.
Como es una biblioteca popular, es muy importante saber que jackson-databind ha tenido muchas vulnerabilidades reportadas en los últimos años. Sin embargo, esto no significa que usar Jackson ObjectMapper sea un riesgo de seguridad por defecto. En este artículo explicaremos cómo funcionan las vulnerabilidades de deserialización de Jackson y cómo asegurarte de que no te afecten.
Pero antes de profundizar, respondamos algunas preguntas que quizá tengas...
¿Qué es la deserialización?
La deserialización consiste en transformar los datos de un archivo o flujo de datos nuevamente en un objeto para usarlo en tu aplicación. Estos datos pueden ser binarios o estar estructurados en formatos como JSON y XML. La deserialización es lo opuesto a la serialización, que transforma objetos en flujos de bytes o texto estructurado.
¿Para qué se usa Jackson ObjectMapper?
Jackson ObjectMapper forma parte de la biblioteca Jackson databind y se usa para transformar JSON en objetos Java y viceversa. Es una de las bibliotecas más utilizadas y conocidas del ecosistema Java para convertir JSON a Java y viceversa, y viene incluida automáticamente con Spring Boot.
¿Jackson ObjectMapper es seguro?
Sí, Jackson ObjectMapper es seguro y utiliza valores predeterminados seguros. Es fundamental actualizarlo a las versiones más recientes para evitar problemas de seguridad conocidos.
¿Es costoso crear un ObjectMapper?
Crear un ObjectMapper es bastante costoso. Por eso, se recomienda reutilizar la instancia de ObjectMapper. Jackson ObjectMapper es seguro para subprocesos, así que puedes reutilizarlo sin problemas.
Usar Jackson ObjectMapper para crear objetos Java a partir de JSON
Con el código siguiente, podemos crear un ObjectMapper y usarlo para volver a crear un Person a partir de una cadena JSON proveniente de un archivo.
Esto es muy sencillo y no sucederá nada complicado. Sin anotaciones, Jackson ObjectMapper usa la reflexión para realizar la asignación a POJO. Gracias a la reflexión, funciona con todos los campos, sin importar el modificador de acceso. Si hay getters y setters disponibles, Jackson ObjectMapper los usará para realizar la asignación.
Tipado predeterminado en Jackson
Muchas de las vulnerabilidades de la biblioteca Jackson para la serialización JSON dependen del tipado predeterminado, que no está habilitado de forma predeterminada. Debes habilitarlo explícitamente. Esto significa que, en la mayoría de los casos, las vulnerabilidades mencionadas en esta lista no afectan directamente a tu sistema.
Pero expliquemos qué es el tipado predeterminado y para qué se usa.
El tipado predeterminado es un mecanismo de Jackson ObjectMapper para manejar tipos polimórficos y herencia. Si quieres deserializar JSON a un POJO de Java, pero no sabes cuál es el subtipo del objeto o del campo, puedes deserializarlo simplemente a la superclase.
Supongamos que tienes Coffee y Tea. Ambas clases tienen la misma superclase: HotDrink. Así que, si tu Breakfast contiene un HotDrink, pero no sabes si es Coffee o Tea, puedes usar el tipado predeterminado para resolverlo.
En este ejemplo, habilité el tipado predeterminado en ObjectMapper para poder manejar el polimorfismo en todos los lugares donde uso este ObjectMapper. También puedes hacerlo en un campo específico mediante la anotación @JsonTypeInfo.
Problemas de seguridad con el tipado predeterminado en Jackson ObjectMapper
Si el tipado predeterminado está habilitado de forma global, es posible llevar la herencia al extremo. Si tu Breakfast no contiene un HotDrink, sino un campo de tipo Object, entonces puede usarse cualquier objeto disponible en el classpath. Esto también significa que podemos deserializar cualquier objeto disponible en el classpath. Potencialmente, podría tratarse de un objeto gadget que configure una cadena de gadgets y que, finalmente, permita la ejecución remota.
Estas cadenas de gadgets son bastante similares a las que describí en mi publicación del blog Serialización y deserialización en Java. Simplifiquemos esto con un solo gadget que ejecuta un comando justo cuando se inicializa. La clase SecondBreakfast de abajo contiene una bebida de tipo Object.
Si deserializo mi SecondBreakfast con el tipado predeterminado habilitado, puedo deserializar un Object que contiene código arbitrario para ejecutar.
Este es un ejemplo simplificado. Aunque encontrar y crear una cadena de gadgets no es fácil, definitivamente es posible. Debido a todas las bibliotecas y los frameworks que usamos, es probable que una combinación de todas las clases de tu classpath sirva para crear una cadena de gadgets de este tipo.
Además, ya se ha identificado un gran conjunto de “clases peligrosas” conocidas. La deserialización de una clase de este tipo se considera peligrosa y, básicamente, sigue el patrón descrito anteriormente. Consulta la lista de vulnerabilidades de deserialización de la biblioteca jackson-databind en Snyk Vulnerability Database.
¿Cómo afecta esto a mi aplicación?
Bien, no entres en pánico: no es tan grave. En primer lugar, quienes mantienen la biblioteca jackson-databind bloquean activamente el conjunto de “clases peligrosas” en SubTypeValidator. En segundo lugar, debes habilitar explícitamente los tipos predeterminados. Esto significa que, de forma predeterminada, esta opción está desactivada y ni siquiera es posible realizar la deserialización polimórfica.
El análisis con herramientas de SCA como Snyk muestra la vulnerabilidad en los resultados. Una búsqueda rápida muestra que en el pasado hubo muchos problemas de este tipo. Siempre es recomendable actualizar a la versión más reciente porque la biblioteca recibe mantenimiento constante y las nuevas “clases peligrosas” se bloquean activamente cuando se detectan. Aun así, lo mejor es evitar habilitar el tipado predeterminado de tu ObjectMapper de Jackson.
El asistente de triaje de Snyk al rescate
Snyk está trabajando para filtrar estas vulnerabilidades cuando tu código no cumple los requisitos previos. En el caso de Jackson ObjectMapper, esto significa que, si tu código no habilita el tipado polimórfico, te indicaremos que es poco probable que esta vulnerabilidad específica pueda explotarse en tu sistema. Al momento de escribir esto, esta función solo está disponible para la vulnerabilidad de deserialización de Jackson, pero el equipo sigue trabajando para mejorarla y ampliarla. Para usar esta función, necesitamos tu aprobación para analizar tu código y comprobar que el tipado predeterminado no esté habilitado.

Los ejemplos de código de esta publicación del blog están disponibles en mi cuenta de GitHub. Siéntete en libertad de bifurcarlos o reutilizarlos como prefieras.
¡Deserializa de forma segura!
La mejor forma de evitar problemas de deserialización con Jackson ObjectMapper es impedir el tipado polimórfico. No habilites el tipado predeterminado en tu ObjectMapper. Además, conecta tu proyecto a Snyk para descubrir si usas una biblioteca jackson-databind con vulnerabilidades conocidas. En la mayoría de los casos, puedes reemplazarla fácilmente por una versión más reciente. Cuando habilitas el análisis de código, Triage Assistant puede ayudarte a determinar si es probable o improbable que se pueda explotar.
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.



