O formato pickle representa objetos Python como uma sequência de instruções. Carregar um arquivo com pickle.load() executa essas instruções e pode importar funções ou chamar construtores, por isso dados não confiáveis representam risco de execução de código. O módulo pickletools no Python desmonta o fluxo em opcodes legíveis sem executar o pickle, ajudando a estudar protocolos, investigar arquivos e otimizar serializações.
Neste guia, você aprenderá a usar a interface de terminal, dis(), genops() e optimize(), além de reconhecer limites de segurança. O conteúdo complementa nossos artigos sobre pickle no Python, bytecode com dis, shelve, traceback e comparação de arquivos.
Por que pickle exige cuidado
Pickle não é apenas um contêiner passivo de dados. O fluxo contém instruções para reconstruir objetos e pode referenciar globals, callables e mecanismos de redução.
import pickle
with open("dados.pickle", "rb") as arquivo:
objeto = pickle.load(arquivo)Esse código é seguro somente quando o arquivo vem de uma fonte totalmente confiável e sua integridade está protegida. Nunca carregue um upload, anexo ou arquivo baixado aleatoriamente.
O papel de pickletools
Pickletools interpreta a estrutura do formato e exibe seus opcodes sem executar as operações de reconstrução.
A documentação oficial de pickletools informa que o módulo é voltado principalmente a desenvolvedores do pickle e ferramentas de baixo nível, mas suas funções também são úteis em auditoria e aprendizado.
Criar um pickle de teste
import pickle
conteudo = {
"nome": "Ana",
"pontos": [10, 20, 30],
}
dados = pickle.dumps(
conteudo,
protocol=pickle.HIGHEST_PROTOCOL,
)
with open("exemplo.pickle", "wb") as arquivo:
arquivo.write(dados)Use dados criados localmente para aprender. Não execute pickle.loads() apenas para descobrir o conteúdo de um arquivo desconhecido.
Desmontar pela linha de comando
python -m pickletools exemplo.pickleA saída mostra posição, byte do opcode, nome, argumento e informações adicionais. No final, aparece o maior protocolo exigido pelas instruções encontradas.
pickletools versus python -m pickle
python -m pickle carrega o objeto para mostrar sua representação. Isso executa o fluxo e não deve ser usado em arquivos não confiáveis.
python -m pickletools desmonta o formato e é a opção mais segura para inspeção estrutural. Ainda assim, limite tamanho e tempo ao processar entradas hostis.
Anotar cada opcode
A opção -a adiciona descrições curtas.
python -m pickletools -a exemplo.pickleA anotação ajuda a entender o efeito de operações como PROTO, FRAME, MARK, MEMOIZE, BINUNICODE e STOP.
Salvar a desmontagem
python -m pickletools \
-o relatorio.txt \
exemplo.pickleNão confunda o relatório textual com uma versão sanitizada do objeto. Ele ainda pode conter strings, nomes e outros dados sensíveis presentes no arquivo.
Controlar indentação
-l define quantos espaços são usados a cada nível iniciado por MARK.
python -m pickletools -l 2 exemplo.pickleA indentação é apenas visual e não altera o arquivo original.
Vários arquivos e preâmbulo
A opção -p imprime um texto antes de cada arquivo.
python -m pickletools \
-p '=== próximo pickle ===' \
a.pickle b.pickleIsso ajuda a separar relatórios concatenados.
Preservar memo entre arquivos
-m mantém o memo ao desmontar vários fluxos.
python -m pickletools -m parte1.pickle parte2.pickleUse somente quando os pickles foram produzidos por um processo que compartilha memo de forma compatível. Para arquivos independentes, o padrão separado é mais claro.
Ler de stdin
cat exemplo.pickle | python -m pickletools -Em pipelines, evite misturar dados binários e logs no mesmo fluxo. Defina limites de tamanho antes de encaminhar uploads.
Usar dis() programaticamente
pickletools.dis() escreve uma desmontagem simbólica em um objeto de arquivo.
import io
import pickletools
saida = io.StringIO()
pickletools.dis(
dados,
out=saida,
annotate=40,
)
print(saida.getvalue())pickle pode ser bytes ou um objeto de arquivo. out usa sys.stdout por padrão.
Memo programático
O parâmetro memo recebe um dicionário compartilhado entre desmontagens.
memo = {}
pickletools.dis(dados_a, memo=memo)
pickletools.dis(dados_b, memo=memo)O memo representa objetos já armazenados no protocolo. Compartilhá-lo sem que os fluxos tenham relação pode produzir interpretações incorretas.
Iterar opcodes com genops()
genops() fornece triplas (opcode, argumento, posição).
for opcode, argumento, posicao in pickletools.genops(dados):
print(
posicao,
opcode.name,
argumento,
)O objeto OpcodeInfo contém nome, código, documentação, efeito de pilha, protocolo mínimo e outras informações internas.
Criar um relatório estruturado
def relatorio_pickle(dados: bytes):
for opcode, argumento, posicao in pickletools.genops(dados):
yield {
"posicao": posicao,
"opcode": opcode.name,
"protocolo": opcode.proto,
"argumento": repr(argumento),
}Limite o tamanho de repr(argumento), pois strings e blobs podem ser enormes.
Identificar operações sensíveis
Uma auditoria pode sinalizar opcodes relacionados à reconstrução de globals e chamadas.
ALERTAS = {
"GLOBAL",
"STACK_GLOBAL",
"REDUCE",
"BUILD",
"OBJ",
"INST",
"NEWOBJ",
"NEWOBJ_EX",
}
for opcode, argumento, posicao in pickletools.genops(dados):
if opcode.name in ALERTAS:
print("Atenção:", posicao, opcode.name, argumento)Uma allowlist ou blocklist de opcodes não torna o unpickle seguro. Combinações, extensões e objetos permitidos ainda podem criar comportamentos perigosos.
Protocolos
Pickle possui várias versões de protocolo. Protocolos mais novos podem usar frames, memoização eficiente e suporte melhor a objetos grandes.
import pickle
for protocolo in range(pickle.HIGHEST_PROTOCOL + 1):
dados = pickle.dumps({"x": 1}, protocol=protocolo)
nomes = [op.name for op, _, _ in pickletools.genops(dados)]
print(protocolo, nomes)O protocolo não é equivalente à versão do Python, embora versões do interpretador definam quais protocolos são suportados.
PROTO e maior protocolo usado
O opcode PROTO declara uma versão, mas a desmontagem também calcula o maior protocolo exigido pelas operações. Fluxos antigos podem não começar com PROTO.
Ferramentas devem observar a sequência completa, não apenas o primeiro byte.
Frames
Protocolos modernos podem dividir o fluxo com FRAME. Frames ajudam o unpickler a processar blocos e reduzir chamadas de leitura.
Um tamanho de frame declarado não deve levar uma ferramenta a alocar memória ilimitada. Leia entradas em ambiente controlado e imponha tamanho máximo ao arquivo.
Memo
O memo evita serializar repetidamente o mesmo objeto e preserva referências compartilhadas.
lista = []
objeto = [lista, lista]
dados = pickle.dumps(objeto, protocol=4)
pickletools.dis(dados)A desmontagem mostra armazenamento e recuperação da referência. Isso explica por que os dois itens após unpickle apontam para a mesma lista.
Otimizar com optimize()
pickletools.optimize() remove opcodes PUT não utilizados e devolve um pickle equivalente.
otimizado = pickletools.optimize(dados)
print(len(dados), len(otimizado))O resultado pode ocupar menos espaço e ser carregado mais rapidamente. Não aplique a função a dados não confiáveis sem limites, e não carregue o resultado para “validar” segurança.
Verificar equivalência em dados confiáveis
original = pickle.loads(dados)
otimizado_objeto = pickle.loads(otimizado)
assert original == otimizado_objetoEsse teste executa pickle e deve ser feito apenas com dados criados ou autenticados pelo próprio sistema.
Assinar pickles confiáveis
Se uma aplicação realmente precisa armazenar pickle, proteja integridade e autenticidade com HMAC ou assinatura e mantenha a chave separada.
A assinatura impede adulteração detectável, mas não transforma arquivos de terceiros em dados confiáveis. Ela só confirma que o conteúdo foi produzido por uma entidade que possui a chave.
Preferir formatos de dados
Para interoperabilidade ou entrada externa, prefira JSON, MessagePack, Protocol Buffers ou um esquema de banco. Esses formatos representam dados, não instruções arbitrárias de reconstrução Python.
Pickle é apropriado principalmente para comunicação e persistência internas entre componentes confiáveis e compatíveis.
Limites de recurso
Mesmo sem executar opcodes, uma análise pode consumir recursos com arquivos grandes, argumentos extensos ou sequências artificiais.
Imponha tamanho máximo, timeout, limite de opcodes e saída truncada. Para uploads, execute a desmontagem em subprocesso com memória e CPU limitadas.
Exemplo de scanner isolado
from pathlib import Path
def analisar(caminho: Path, limite=10_000_000):
if caminho.stat().st_size > limite:
raise ValueError("Arquivo grande demais")
with caminho.open("rb") as arquivo:
for indice, (op, arg, pos) in enumerate(
pickletools.genops(arquivo)
):
if indice > 100_000:
raise ValueError("Muitos opcodes")
yield pos, op.name, argAbra somente arquivos regulares dentro de uma raiz autorizada e evite seguir symlinks inesperados.
pickletools não valida segurança
A desmontagem ajuda analistas a compreender o fluxo, mas não prova que ele é inofensivo. A semântica final depende dos objetos importados, reducers e código disponível no ambiente.
A regra permanece: nunca faça unpickle de uma fonte não confiável.
Erros frequentes
- Usar
python -m picklepara arquivo desconhecido. - Carregar o objeto após desmontar para confirmar conteúdo.
- Confiar em uma blocklist pequena de opcodes.
- Compartilhar memo entre arquivos independentes.
- Gerar relatórios sem truncar argumentos.
- Tratar optimize como sanitização.
- Ignorar tamanho e quantidade de operações.
- Presumir compatibilidade eterna entre versões.
Boas práticas
- Use pickletools para inspeção não executável.
- Mantenha arquivos desconhecidos longe de pickle.load.
- Limite tamanho, tempo, opcodes e saída.
- Execute análises hostis em subprocesso isolado.
- Use formatos de dados para interfaces externas.
- Assine apenas pickles produzidos internamente.
- Registre protocolo e versão do Python.
- Teste otimização somente com dados confiáveis.
Conclusão
O módulo pickletools no Python revela as instruções que formam um pickle sem executar seu bytecode. A interface de terminal e dis() produzem desmontagens legíveis, genops() permite análise estruturada e optimize() remove memoizações desnecessárias.
Essa visibilidade ajuda a aprender o formato e investigar arquivos, mas não torna pickle seguro. A proteção real vem de não carregar entradas desconhecidas, impor limites e preferir formatos puramente declarativos nas fronteiras do sistema.







