Deserialización insegura: riesgos y defensa
Riesgos de pickle, Java Serialization y PHP unserialize con datos no confiables, y cómo sustituirlos o limitarlos.
💡 Explicación sencilla (ELI5)
La serialización es como enviar por correo instrucciones para montar un modelo. La deserialización sigue esas instrucciones para reconstruir los datos. Algunos formatos también pueden nombrar clases o ejecutar hooks durante la reconstrucción; aceptar instrucciones no confiables puede activar comportamientos no previstos. El impacto depende del formato, runtime, clases disponibles y contexto de la aplicación.
Core Concepts & Key Terms
- Serialización / deserialización
- Convertir datos u objetos en una representación para almacenarlos o transmitirlos y luego analizarlos o reconstruirlos en el programa.
- Hooks de deserialización
- Callbacks como Python __reduce__, Java readObject o PHP __wakeup / __unserialize que pueden ejecutarse al reconstruir objetos.
- Gadget chain
- Secuencia de comportamientos de bibliotecas presentes que puede encadenarse para producir un efecto no previsto. Depende de la aplicación y de sus versiones.
- Filtro de deserialización
- Política que limita clases o métricas del grafo al leer objetos. Reduce el riesgo, pero no convierte los flujos nativos no confiables en un buen formato.
How Unsafe Deserialization Can Become an Exploit Chain
Bytes no confiables llegan al decodificador
Una cookie, cuerpo de API, mensaje de cola o registro contiene datos que un atacante puede influir.
La aplicación invoca un formato de objeto nativo
El código entrega esos bytes a un decodificador como Python pickle, Java ObjectInputStream o PHP unserialize().
La reconstrucción activa comportamiento del formato
Según el formato y las clases alcanzables, constructores, hooks, autoloading o métodos de biblioteca pueden ejecutarse antes de la validación de la aplicación.
El impacto depende del comportamiento alcanzable
Un gadget o hook disponible puede producir efectos no autorizados; otros datos solo fallan o consumen recursos. La ejecución de código no es automática en toda aplicación.
Python / Flask: carga insegura de objetos frente a validación de JSON primitivo
# VULNERABLE: pickle can execute behavior during unpickling.
from flask import Flask, request
import pickle
app = Flask(__name__)
@app.post("/api/session")
def load_session():
# Never deserialize attacker-controlled bytes with pickle.
session = pickle.loads(request.get_data())
return {"user_id": session.user_id}
# SAFER: parse JSON primitives, then validate expected shape.
from flask import Flask, abort, request
app = Flask(__name__)
app.config["MAX_CONTENT_LENGTH"] = 16 * 1024
@app.post("/api/session")
def load_session():
payload = request.get_json()
if not isinstance(payload, dict):
abort(400, "Expected a JSON object")
user_id = payload.get("user_id")
if type(user_id) is not int or user_id <= 0:
abort(400, "Invalid user_id")
# Resolve authorization and session state server-side.
return {"user_id": user_id}
Lista de comprobación de endurecimiento
JSON también requiere límites de tamaño y validación de estructura. En formatos de objetos heredados, no confíes solo en listas de clases: autentica los bytes exactos antes de deserializar y protege las claves. Un MAC no cifra los datos ni evita la repetición.
- No deserialices flujos nativos no confiables con Python pickle o PHP unserialize; prefiere datos primitivos JSON con validación de esquema y tamaño.
- Si aún no puedes retirar la serialización Java, configura una allowlist ObjectInputFilter y límites del grafo; el filtro no se activa automáticamente en todas las instalaciones.
- Para proteger la integridad de datos heredados, autentica los bytes exactos antes de deserializar mediante una clave MAC protegida; un MAC no cifra ni evita replay.
- Mantén la autoridad de sesión en el servidor o usa tokens firmados con una biblioteca mantenida, expiración, audience y rotación de claves.