Desserialização insegura: riscos e correção
Riscos do pickle, Java Serialization e PHP unserialize com dados não confiáveis, e como substituí-los ou restringi-los.
💡 Explicação em linguagem simples (ELI5)
Serializar é como enviar pelo correio instruções para montar um modelo. Desserializar segue essas instruções para reconstruir os dados. Alguns formatos também podem indicar classes ou chamar hooks durante a reconstrução; aceitar instruções não confiáveis pode acionar comportamentos que o receptor não pretendia. O impacto depende do formato, runtime, classes disponíveis e contexto da aplicação.
Core Concepts & Key Terms
- Serialização / desserialização
- Converter dados ou objetos em uma representação para armazenar ou transmitir e depois analisá-los ou reconstruí-los no programa.
- Hooks de desserialização
- Callbacks como Python __reduce__, Java readObject ou PHP __wakeup / __unserialize que podem rodar ao reconstruir objetos.
- Gadget chain
- Sequência de comportamentos de bibliotecas já presentes que pode ser encadeada para gerar um efeito não intencional. A disponibilidade depende da aplicação e da versão.
- Filtro de desserialização
- Política que restringe classes e métricas do grafo ao ler um stream de objetos. Reduz o risco, mas não torna streams nativos não confiáveis uma boa escolha.
How Unsafe Deserialization Can Become an Exploit Chain
Bytes não confiáveis chegam ao decodificador
Um cookie, corpo de API, mensagem de fila ou registro contém dados que um atacante pode influenciar.
A aplicação chama um formato de objeto nativo
O código passa os bytes a um decodificador como Python pickle, Java ObjectInputStream ou PHP unserialize().
A reconstrução aciona comportamento específico do formato
Conforme o formato e as classes acessíveis, construtores, hooks, autoloading ou métodos de biblioteca podem rodar antes da validação da aplicação.
O impacto depende do comportamento alcançável
Um gadget ou hook utilizável pode causar efeitos não autorizados; outras entradas podem apenas falhar ou consumir recursos. Execução de código não é automática em todo formato ou aplicação.
Python / Flask: carregamento inseguro de objetos vs. validação 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}
Checklist de proteção de engenharia
Para limitar o impacto, JSON ainda exige validação de tamanho e estrutura. Para formatos de objetos legados, não confie em listas de classes como fronteira única; autentique os bytes exatos antes da desserialização e mantenha as chaves protegidas. MAC não cifra os dados nem evita replay.
- Não desserialize streams de objetos nativos não confiáveis com Python pickle ou PHP unserialize; prefira dados primitivos como JSON com validação de schema e tamanho.
- Se a serialização Java ainda não puder ser removida, configure uma allowlist ObjectInputFilter e limites de grafo; o filtro não é ativado automaticamente em toda implantação.
- Para integridade de payloads legados, autentique os bytes exatos antes de desserializar com uma chave MAC bem gerenciada; MAC não fornece sigilo nem evita replay.
- Mantenha a autoridade da sessão no servidor ou use tokens assinados mantidos por framework com expiração, audience e rotação de chaves.