A08:2021 – Fallos de Integridad de Software y Datos (Software and Data Integrity Failures) es otra categoría reorganizada en 2021: nace de asumir que muchas brechas ocurren porque el software confía en datos o en código sin verificar su integridad. Aquí caben tres cosas relacionadas: la deserialización insegura (que en 2017 era una categoría propia y ahora vive dentro de A08), la falta de verificación de integridad en actualizaciones y pipelines de CI/CD, y el uso de datos o dependencias sin comprobar su procedencia.
El hilo común es la confianza no verificada. Si el módulo Java de BazarNube deserializa un objeto que vino de fuera sin comprobar nada, puede acabar ejecutando código del atacante. Si el pipeline despliega un artefacto sin verificar su firma, puede desplegar uno manipulado. En esta lección atacamos ambos frentes: la deserialización en el legacy Java y un pipeline sin verificación de integridad.
Aviso legal y ético: los ejemplos de explotación son ilustrativos y con datos ficticios. Practica solo sobre sistemas propios o con autorización explícita. La deserialización insegura puede derivar en ejecución remota de código: trátala con extremo cuidado y solo en entornos controlados.
Contenido
- Qué es un fallo de integridad
- Deserialización insegura: el caso del módulo Java
- Cómo se explota una deserialización (ilustrativo)
- Prevención de la deserialización insegura
- Integridad de actualizaciones y del pipeline CI/CD
- Dependencias y datos sin verificar
- Errores comunes, ejercicios y soluciones
- Qué es un fallo de integridad
La integridad garantiza que un dato o un artefacto no ha sido alterado y proviene de quien dice provenir. Un fallo de integridad ocurre cuando el sistema actúa sobre algo sin comprobarlo: deserializa un objeto arbitrario, instala una actualización sin firma, ejecuta un script descargado sin verificar su hash. La defensa transversal es verificar antes de confiar: firmas digitales, hashes, canales autenticados.
- Deserialización insegura: el caso del módulo Java
Serializar es convertir un objeto en bytes para guardarlo o transmitirlo; deserializar es el proceso inverso. El peligro aparece cuando deserializas datos controlados por el atacante con un mecanismo que puede instanciar clases y ejecutar lógica durante el proceso. La serialización nativa de Java es el ejemplo clásico.
El módulo legacy de BazarNube guarda el estado del carrito en una cookie serializada con Java, "para no tocar la base de datos":
// CartController.java (modulo legacy) — VULNERABLE a deserializacion insegura
public Cart loadCart(String cookieValue) throws Exception {
byte[] data = Base64.getDecoder().decode(cookieValue);
ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data));
return (Cart) ois.readObject(); // deserializa lo que venga en la cookie
}El problema: ObjectInputStream.readObject() sobre datos del cliente. La cookie la controla el usuario; puede sustituir el carrito serializado por un objeto malicioso que aproveche una "gadget chain" (una cadena de clases presentes en el classpath cuyo proceso de deserialización desemboca en ejecución de comandos).
- Cómo se explota (ilustrativo)
El atacante no envía un Cart; envía un objeto serializado especialmente construido con herramientas conocidas (del tipo que generan cadenas de gadgets a partir de librerías comunes del classpath). Al llamar a readObject(), la deserialización recorre esa cadena y, como efecto colateral, ejecuta un comando en el servidor —por ejemplo, abrir una conexión inversa o leer ficheros. No hace falta acertar ninguna contraseña: basta con que el endpoint deserialice datos no confiables y que en el classpath exista una cadena explotable.
flowchart LR A[Atacante crea objeto serializado malicioso] --> B[Lo envia en la cookie del carrito] B --> C[readObject en el servidor] C --> D[Gadget chain del classpath] D --> E[Ejecucion de comandos en el servidor]
Es de las vulnerabilidades más severas: suele significar ejecución remota de código.
- Prevención de la deserialización insegura
La regla de oro: no deserialices datos no confiables con mecanismos que puedan instanciar objetos arbitrarios. En orden de preferencia:
- No serialices objetos en el cliente. Guarda solo un identificador y recupera el estado del servidor. El carrito debe vivir en la base de datos, referenciado por un id de sesión.
- Usa formatos de datos, no de objetos. JSON con un parser que no instancie tipos arbitrarios (sin polymorphic type handling activado). Deserializa a una estructura conocida y valida.
- Si es imprescindible la serialización binaria, aplica allowlist de clases permitidas (
ObjectInputFilteren Java) y verifica la integridad del dato con una firma/HMAC.
Corrección: estado en servidor + JSON validado
// CartController.java — SEGURO: la cookie solo lleva un id opaco
public Cart loadCart(String sessionId) {
// El estado real vive en la base de datos, no en el cliente
return cartRepository.findBySession(sessionId)
.orElseGet(Cart::new);
}Y si en algún flujo hubiese que aceptar JSON del cliente, deserializar a un tipo concreto sin habilitar el manejo polimórfico de tipos:
// JSON seguro: mapeo a un tipo conocido, sin default typing
ObjectMapper mapper = new ObjectMapper();
// NO habilitar activateDefaultTyping(): eso reintroduce el riesgo de gadgets
CartDto dto = mapper.readValue(json, CartDto.class);
validate(dto);Como refuerzo (defensa en profundidad) para datos que deban ir y volver del cliente, firma su integridad:
// HMAC para detectar manipulacion de un dato que viaja al cliente
String payload = base64(json);
String mac = hmacSha256(SECRET, payload); // clave desde el gestor de secretos
// Al recibir: recomputar el HMAC y comparar en tiempo constante antes de usar el dato| Enfoque | Seguridad |
|---|---|
ObjectInputStream sobre datos del cliente |
Muy peligroso (RCE) |
| JSON con default typing activado | Peligroso (gadgets) |
| JSON a tipo concreto + validación | Seguro |
| Estado en servidor, solo id en cliente | Seguro (recomendado) |
| Dato firmado (HMAC/firma) + validación | Seguro para datos que deben viajar |
- Integridad de actualizaciones y del pipeline CI/CD
La integridad no acaba en la deserialización. El pipeline que construye y despliega BazarNube es un objetivo de alto valor: si un atacante inyecta código en el proceso de build o sustituye un artefacto, compromete a todos los usuarios de una sola vez (ataque a la cadena de suministro).
El pipeline de BazarNube tenía descuidos típicos:
# pipeline VULNERABLE (resumen)
steps:
- run: curl -s https://example.test/install.sh | bash # ejecuta script sin verificar
- run: docker pull miregistro/app:latest # tag mutable, sin firma
- deploy: kubectl apply -f k8s/ # sin verificar el artefactoProblemas: ejecutar un script descargado sin verificar su hash, usar imágenes con tag mutable (latest, que puede cambiar bajo tus pies) y desplegar artefactos sin verificar su firma. Versión con controles de integridad:
# pipeline SEGURO (resumen)
steps:
- run: |
curl -s -o install.sh https://example.test/install.sh
echo "<hash_esperado> install.sh" | sha256sum -c - # verifica integridad
bash install.sh
- run: docker pull miregistro/app@sha256:<digest> # imagen por digest inmutable
- run: cosign verify miregistro/app@sha256:<digest> # verifica la firma del artefacto
- deploy: kubectl apply -f k8s/Controles clave de integridad en CI/CD:
- Fijar artefactos por digest inmutable, no por tags mutables.
- Firmar y verificar artefactos e imágenes (firma de artefactos, provenance del build).
- Verificar hashes de todo lo que se descargue en el pipeline.
- Proteger el pipeline como código sensible: mínimos permisos, secretos gestionados, revisión de cambios en la propia definición del pipeline.
- Dependencias y datos sin verificar
A08 se solapa con A06 (componentes) en la parte de procedencia: instalar dependencias sin verificar su integridad (ignorar los hashes del lockfile, usar registros no confiables) es un fallo de integridad. Asegúrate de:
- Respetar los hashes de integridad de los lockfiles (
package-lock.json,pom.xmlcon checksums). - Instalar desde registros controlados (proxy/registro interno) y no desde fuentes arbitrarias.
- Verificar la firma de artefactos críticos cuando esté disponible.
Errores Comunes y Consejos
- Deserializar datos del cliente con
ObjectInputStream. Evítalo; guarda estado en servidor. - Activar el default typing de JSON. Reintroduce el riesgo de gadgets; deserializa a tipos concretos.
- Confiar en tags mutables (
latest). Usa digests inmutables y firmas. - Descargar y ejecutar scripts sin verificar hash. Verifica siempre integridad de lo descargado.
- Tratar el pipeline como algo no crítico. Es un objetivo de cadena de suministro; protégelo.
- Ignorar los hashes de los lockfiles. Son tu verificación de integridad de dependencias.
- Consejo: aplica "verificar antes de confiar" a todo dato o artefacto que cruce un límite: objetos, actualizaciones, imágenes, dependencias.
Ejercicios
Ejercicio 1. ¿Por qué guardar el carrito como objeto Java serializado en una cookie es peligroso, y cómo lo rediseñarías para eliminar la clase de vulnerabilidad?
Ejercicio 2. Un pipeline hace docker pull miregistro/app:latest y despliega. Enumera dos problemas de integridad y su corrección.
Ejercicio 3. Un compañero propone "solucionar" la deserialización cifrando la cookie con AES. ¿Elimina el riesgo de deserialización insegura? Matiza.
Soluciones
Solución 1. Es peligroso porque readObject() sobre datos que el usuario controla puede instanciar una gadget chain del classpath y derivar en ejecución de comandos (RCE). Rediseño: no serializar objetos en el cliente; guardar el estado del carrito en la base de datos y enviar al cliente solo un identificador opaco de sesión. Así el servidor nunca deserializa datos no confiables.
Solución 2. (1) Tag mutable latest: el contenido puede cambiar sin que lo sepas → usar @sha256:<digest> inmutable. (2) Sin verificación de firma: podrían haber sustituido la imagen → firmar en el build y verificar (cosign verify) antes de desplegar. Además, restringir el registro de origen.
Solución 3. Ayuda pero no elimina la clase por sí sola. Cifrar/firmar con una clave secuestrada evita que un atacante externo fabrique la cookie (si la clave está bien protegida), y es una defensa válida (integridad/confidencialidad). Pero si la clave se filtra, o el flujo acepta datos serializados por otras vías, el riesgo de RCE por deserialización persiste. La solución de fondo sigue siendo no deserializar objetos no confiables: firmar es defensa en profundidad, no sustituto del rediseño.
Conclusión
A08 nos enseña a no confiar sin verificar. En BazarNube eliminamos la deserialización insegura del carrito llevando el estado al servidor y usando JSON mapeado a tipos concretos, y endurecimos el pipeline con artefactos por digest, firmas verificadas y hashes de todo lo descargado. El principio "verificar antes de confiar" cubre objetos, actualizaciones, imágenes y dependencias.
Entrada de backlog — A08: carrito rediseñado (estado en BD, solo id en cliente); prohibido ObjectInputStream sobre datos externos y el default typing de JSON; pipeline con imágenes por digest, cosign verify y verificación de hashes; respeto de hashes de lockfiles.
Hemos prevenido muchas cosas. Pero ninguna prevención es perfecta: cuando algo ocurra, necesitamos verlo. La siguiente lección aborda A09:2021 – Fallos de Registro y Monitorización, con el logging de la API de BazarNube y su enlace con el caso de incidente del módulo 8.
Curso de OWASP: Directrices y Estándares para la Seguridad en Aplicaciones Web
Módulo 1: Introducción a OWASP
Módulo 2: Principales Proyectos de OWASP
- OWASP Top Ten
- OWASP ASVS (Application Security Verification Standard)
- OWASP SAMM (Software Assurance Maturity Model)
- OWASP ZAP (Zed Attack Proxy)
- Otros Proyectos Clave: WSTG, Cheat Sheets y Dependency-Check
Módulo 3: OWASP Top Ten 2021 en Profundidad
- A01:2021 – Pérdida de Control de Acceso
- A02:2021 – Fallos Criptográficos y Exposición de Datos Sensibles
- A03:2021 – Inyección
- Cross-Site Scripting (XSS) en Profundidad
- A04:2021 – Diseño Inseguro
- A05:2021 – Configuración de Seguridad Incorrecta
- Entidades Externas XML (XXE)
- A06:2021 – Componentes Vulnerables y Desactualizados
- A07:2021 – Fallos de Identificación y Autenticación
- A08:2021 – Fallos de Integridad de Software y Datos (Deserialización Insegura)
- A09:2021 – Fallos de Registro y Monitorización
- A10:2021 – Server-Side Request Forgery (SSRF)
Módulo 4: OWASP ASVS (Application Security Verification Standard)
- Introducción a ASVS
- Niveles de Verificación
- Requisitos de Seguridad
- Implementación de ASVS en Proyectos
Módulo 5: OWASP SAMM (Software Assurance Maturity Model)
Módulo 6: OWASP ZAP (Zed Attack Proxy)
- Introducción a ZAP
- Instalación y Configuración
- Escaneo de Vulnerabilidades
- Automatización de Pruebas de Seguridad
Módulo 7: Buenas Prácticas y Recomendaciones
- Ciclo de Vida de Desarrollo Seguro (SDLC)
- Modelado de Amenazas (Threat Modeling)
- Integración de Seguridad en DevOps (DevSecOps)
- Capacitación y Concienciación en Seguridad
- Herramientas y Recursos Adicionales
Módulo 8: Ejercicios Prácticos y Casos de Estudio
- Ejercicio 1: Identificación de Vulnerabilidades
- Ejercicio 2: Implementación de Controles de Seguridad
- Caso de Estudio 1: Análisis de un Incidente de Seguridad
- Caso de Estudio 2: Mejora de la Seguridad en una Aplicación Web
