Pular para o conteúdo
>_ITDITDPlataforma de Segurança Web

Guias de Segurança

Desserialização insegura: o risco de deixar dados externos escolherem o tipo do objeto, e como evitar

Transformar bytes de volta em objetos pode, dependendo do formato, deixar dados externos decidirem qual classe é criada. A OWASP diz que ataques a desserializadores já permitiram negação de serviço, contorno de controle de acesso e execução remota de código — e que o BinaryFormatter do .NET não pode ser tornado seguro.

Publicado 2026-09-05 Atualizado 2026-10-06 Última verificação 2026-10-06 15 min de leitura

Transformar bytes recebidos de volta em objetos não fica perigoso porque o conteúdo está malformado. Em alguns formatos, os dados externos decidem que tipo de objeto é construído.

Por que a validação de entrada chega tarde demais

Formato só de dados (JSON / XML)

os dados externos decidem só os valores → você recebe e depois mapeia para os seus próprios tipos → a validação funciona

Formato que restaura objetos

os dados externos decidem os valores e o tipo → o código do tipo escolhido roda durante a restauração → a execução nunca chega às suas verificações

As verificações de valor acontecem depois que o objeto existe. Num formato que escolhe tipos, a essa altura já acabou.

A maior parte da validação de entrada pergunta se o valor é aceitável. Mas, num formato que restaura objetos, a própria restauração — qual classe é instanciada e que código roda no caminho — é guiada pelos dados externos. Algo pode terminar de executar antes mesmo de a sua validação ser chamada.

É o mesmo formato do prototype pollution: dados externos decidindo a estrutura em vez dos valores. Nome diferente, linguagem diferente, mesmo raciocínio para a defesa.

O que dá errado (os três impactos que a OWASP lista)

Impactos observados em ataques a desserializadores
Negação de serviço
Tornar a própria restauração cara, ou quebrá-la, para derrubar a aplicação
Contorno de controle de acesso
Objetos que representam permissões ou papéis construídos do jeito que convém ao atacante
Execução remota de código
Encadear as rotinas chamadas durante a restauração para chegar à execução de código arbitrário — o resultado mais grave

"O nosso framework não faz isso" não é uma suposição segura

Esta não é uma história só de linguagens antigas. Quando contamos as vulnerabilidades do Next.js nos dados do NVD, a desserialização (CWE-502) apareceu no alto da distribuição de CWEs — porque frameworks modernos trazem caminhos que desserializam o corpo da requisição, entre eles as Server Actions (Next.js e React têm muitas vulnerabilidades?). "Eu não escrevi isso" não significa "isso não está lá".

Duas defesas

1. Não deixe nada escolher tipos (primeira opção)

Nas palavras da OWASP: "Ao trocar para um formato só de dados, como JSON ou XML, você reduz a chance de uma lógica de desserialização personalizada ser reaproveitada para fins maliciosos."

Receba a entrada externa num formato que carrega só valores e depois mapeie para os seus próprios tipos no seu próprio código. Só isso já impede o atacante de escolher o que é construído. Na maioria dos códigos, isso resolve o problema.

2. Assine e recuse o que não estiver assinado

Onde um formato personalizado for inevitável, a orientação observa que você "pode assiná-las como parte do processo de serialização" e então "optar por não desserializar nenhuma mensagem que não tenha assinatura autenticada".

O ponto é verificar antes de restaurar. Restaurar primeiro e inspecionar depois já pode ser tarde demais, pelo motivo acima. A ordem é todo o controle.

Alguns mecanismos não podem ser tornados seguros por configuração

Sobre o BinaryFormatter do .NET, a OWASP afirma diretamente que "o tipo BinaryFormatter é perigoso e não pode ser tornado seguro".

Essa frase importa na prática: existem ferramentas para as quais "está tudo bem se você usar corretamente" simplesmente não é verdade. Opções de reforço, validação adicional, uma allow-list — nada disso a torna segura. O único controle é não usá-la.

Por isso, ao auditar, separe primeiro o que a configuração consegue proteger do que ela não consegue. Gastar esforço na segunda categoria não é mitigação; é adiamento.

O que a OWASP cita, por linguagem

Citados como perigosos (resumidos do ponto de vista de quem defende)
PHP
Evite unserialize(); prefira JSON
Python
pickle / c_pickle, o load do PyYAML e o jsonpickle são listados como perigosos
Java
Sobrescreva ObjectInputStream#resolveClass() para restringir quais classes podem ser restauradas
.NET
O BinaryFormatter é perigoso e não pode ser tornado seguro — não o use

O ponto em comum: o mecanismo prático de persistência embutido numa linguagem é justamente o que nunca deve ficar exposto a dados externos. Ele foi feito para mover objetos inteiros entre partes que confiam umas nas outras, e não supõe um remetente hostil.

APIs seguras e inseguras por linguagem

A tabela mostra lado a lado o que a documentação oficial de cada linguagem avisa para não usar com dados externos e o que ela recomenda no lugar. Se algo da coluna da esquerda aparece no seu código, descubra de onde vêm os dados dele.

LinguagemNão use com dados externosUse no lugarO que dizem os documentos oficiais
Pythonpickle.load / pickle.loads, marshal, yaml.load (com Loader=yaml.Loader) e yaml.unsafe_load, jsonpickle.decodejson.loads, yaml.safe_loadO pickle "não é seguro"; assine com hmac se precisar detectar adulteração
PHPunserialize() (mesmo com allowed_classes definido)json_decode() / json_encode()Não passe entrada não confiável, independentemente de allowed_classes
RubyMarshal.loadJSON, ou outro formato que só carregue tipos básicosCarregar de uma fonte não confiável pode levar a execução remota de código
JavaObjectInputStream#readObject, XMLDecoder, XStream#fromXML com dados externosMapeie JSON para as suas próprias classes (DTOs)Se for inevitável, limite as classes permitidas com ObjectInputFilter
.NETBinaryFormatter, SoapFormatter, NetDataContractSerializer, LosFormatter, ObjectStateFormatter, TypeNameHandling do Json.NET com valor diferente de NoneSystem.Text.Json, XmlSerializer, DataContractSerializerA partir do .NET 9, o BinaryFormatter lança exceção quando usado

Lado a lado, a diferença fica assim. A versão insegura transforma o que chegou direto de volta num objeto; a versão segura lê como valores e passa para o seu próprio tipo só os campos de que você precisa.

# Evite: transformar os bytes recebidos direto de volta num objeto
order = pickle.loads(request_body)
 
# Use: leia os valores e passe para o seu próprio tipo só os campos necessários
data = json.loads(request_body)
order = Order(id=int(data["id"]), quantity=int(data["quantity"]))

Na versão segura, o seu código (Order) decide qual classe é criada. Os dados recebidos só decidem os valores de id e quantity, e a validação de entrada comum funciona sobre eles.

O que verificar no seu próprio código

1

Liste todos os lugares onde os dados externos chegam primeiro

Corpos de requisição, cookies, sessões, filas, uploads, webhooks, caches — todo ponto em que algo é guardado e depois restaurado. Sessões e caches são os que mais passam despercebidos: dados que você considera seus podem ser alcançáveis de fora, dependendo do caminho.

2

Descubra quais deles usam um formato que restaura tipos

Procure as funções e classes citadas na lista acima. Elas são as primeiras coisas a remover. Se um mecanismo que não pode ser tornado seguro (BinaryFormatter e similares) estiver em uso, essa é a prioridade máxima — só a migração resolve.

3

Passe para um formato só de dados

Receba JSON ou XML e mapeie para os seus próprios tipos no seu próprio código. Aplique validação de esquema nesse mapeamento e você fecha ao mesmo tempo as chaves inesperadas (a mesma solução do prototype pollution).

4

Onde não puder mudar, proteja com uma assinatura

Se o formato não puder mudar, assine no momento da serialização e verifique antes de restaurar. Faça de "sem assinatura, sem restauração" o padrão. Trate a chave como em o que é perigoso no .env e nas chaves de API — nunca no mesmo lugar que o código.

5

Conte a restauração que as suas dependências fazem

Mesmo onde você não escreveu nenhuma, uma dependência pode estar restaurando objetos. CVEs classificados como CWE-502 continuam aparecendo, então coloque-os sob monitoramento automático (primeiros passos com o osv-scanner / um guia prático de correção de CVEs).

Como verificar o seu próprio ambiente

Aqui a parte de "encontrar" dos passos acima vira comandos e configurações concretos. Todas as verificações só leem o seu próprio repositório e os seus próprios servidores.

1

Faça uma busca de texto no código-fonte

Comece listando todos os candidatos. Com o ripgrep (rg), as buscas ficam assim (grep -rnE aceita os mesmos padrões).

# Python
rg -n "pickle\.loads?\(|marshal\.loads?\(|yaml\.load\(|yaml\.unsafe_load|jsonpickle\.decode"
# PHP
rg -n "unserialize\("
# Ruby
rg -n "Marshal\.load"
# Java
rg -n "ObjectInputStream|readObject\(|XMLDecoder|fromXML\("
# .NET
rg -n "BinaryFormatter|SoapFormatter|NetDataContractSerializer|LosFormatter|ObjectStateFormatter|TypeNameHandling"

Nem todo resultado é perigoso. Para cada um, anote de onde vêm os dados que ele recebe, e coloque em primeiro lugar tudo o que estiver ligado a um caminho que pessoas de fora conseguem alcançar: requisições, cookies, uploads, armazenamento compartilhado.

2

Procure avisos suprimidos

No .NET, usar o BinaryFormatter gera o aviso ou erro SYSLIB0011. Uma configuração que o suprime marca um lugar onde ainda há uma chamada perigosa no código.

rg -n "SYSLIB0011|CA23[0-3][0-9]" --glob "*.cs" --glob "*.csproj" --glob ".editorconfig" --glob "*.props"

Se encontrar #pragma warning disable SYSLIB0011 ou o ID dentro de <NoWarn>, confirme por que ele está ali e quando será migrado.

3

Ative regras de análise estática

Para Python, as verificações relevantes do Bandit são B301 (pickle e similares), B302 (marshal) e B506 (yaml.load); bandit -r . -t B301,B302,B506 roda só essas três. Para .NET, as regras de análise de código relevantes são as da série CA2300 (por exemplo, a CA2326, que aponta o TypeNameHandling do Json.NET). A CA2326 não vem ativada por padrão nem no .NET 10, então ative-a no .editorconfig com uma linha como dotnet_diagnostic.CA2326.severity = warning. Rode essas regras no CI e novas chamadas também serão barradas.

4

Em Java, verifique as configurações de filtro em tempo de execução

O Java pode restringir, em tempo de execução, quais classes a desserialização pode criar (filtros de serialização). O mecanismo chegou no JDK 9 (JEP 290) e passou a poder ser trocado por contexto no Java 17 (JEP 415). Para um processo em execução, procure jdk.serialFilter na saída de jcmd <PID> VM.system_properties. Ele também pode ser definido como propriedade de segurança no arquivo java.security, então verifique os dois. Se não estiver em nenhum dos dois lugares e o seu código não chamar ObjectInputFilter.Config.setSerialFilter, não há filtro para o processo inteiro.

5

Verifique vulnerabilidades conhecidas nas dependências

Mesmo sem nenhuma chamada desse tipo no seu código, uma dependência pode restaurar objetos. Liste as vulnerabilidades conhecidas nas suas dependências com o osv-scanner ou uma ferramenta similar, e atualize primeiro as classificadas como CWE-502 (desserialização de dados não confiáveis).

Erros comuns e como corrigi-los

ErroPor que é um problemaCorreção
Confiar em dados porque "são internos" ou "eu mesmo salvei este arquivo"A Microsoft dá exemplos de dados que cruzam uma fronteira de confiança sem o desenvolvedor perceber: arquivos salvos compartilhados, sincronização na nuvem, a máquina comprometida de um funcionárioSe algum caminho de fora consegue alcançá-los, trate-os como dados externos. Não restaure nada sem assinatura
Adicionar classes perigosas a uma lista de bloqueio uma por umaNão faz nada contra combinações que não estão na lista ou ainda não são conhecidas. O manual do PHP diz para não passar entrada não confiável mesmo com allowed_classesMude o formato para JSON ou similar. Só onde for inevitável, liste as classes que você permite
Restaurar primeiro e depois validar o conteúdoO código roda durante a restauração, então a validação chega tarde demaisColoque a verificação da assinatura antes da restauração
Suprimir o aviso e considerar corrigidoSilenciar SYSLIB0011 ou CA2326 deixa a chamada perigosa no lugarProcure as supressões e defina um prazo para a migração
Achar que "é JSON, então é seguro"Configurações que deixam o JSON nomear os próprios tipos, como o TypeNameHandling do Json.NET, o transformam num formato que escolhe tiposMantenha o TypeNameHandling em None (o padrão)
Usar yaml.load com PyYAMLA documentação do PyYAML diz que o yaml.load é tão poderoso quanto o pickle e pode chamar qualquer função PythonSubstitua por yaml.safe_load

Um exemplo real: restauração dentro de uma extensão

O boletim deste site sobre a CVE-2026-45247 trata de uma injeção de objetos PHP (CWE-502) na extensão do Magento 2 "Mirasvit Full Page Cache Warmer" anterior à 1.11.12. Ela permite execução remota de código sem autenticação, tem pontuação CVSS 9.3 e foi adicionada ao catálogo de vulnerabilidades sabidamente exploradas (KEV) da CISA.

A lição é que o código de restauração não foi escrito pelos operadores do site; estava dentro de uma extensão que eles instalaram. É por isso que o passo "verifique as dependências" acima importa. A correção foi atualizar a extensão, e buscar no próprio código não a teria encontrado.

A visão deste site: pergunte o que a entrada pode decidir, não se o valor é válido

Diga "validação de entrada" e a maioria das pessoas imagina verificar o valor — tamanho, formato, faixa. A ameaça deste artigo se resolve antes desse ponto. A nossa forma de ver: a pergunta real sobre uma entrada não é "este valor está correto", mas "quanto eu deixei esta entrada decidir".

Só o valor? A estrutura também (prototype pollution)? O tipo também (este artigo)? Até onde ela vai parar (vulnerabilidades de upload de arquivos)? Conte as decisões que você entregou ao lado de fora, e os lugares perigosos aparecem sozinhos.

Fontes (primárias)

  • OWASP Cheat Sheet Series, "Deserialization Cheat Sheet" — cheatsheetseries.owasp.org (os três impactos, a troca para um formato só de dados, a assinatura para integridade, as notas por linguagem e a afirmação sobre o BinaryFormatter vêm todos deste documento; verificado em 5 de setembro de 2026)
  • Documentação do Python, "pickle" — docs.python.org (o aviso de que "não é seguro"; assinar com hmac e preferir JSON)
  • PyYAML Documentation — pyyaml.org (a diferença entre yaml.load e yaml.safe_load)
  • Manual do PHP, "unserialize" — php.net (não passar entrada não confiável, independentemente de allowed_classes; usar JSON)
  • Documentação do Ruby, "Marshal" — docs.ruby-lang.org
  • Microsoft Learn, "Deserialization risks in use of BinaryFormatter and related types" — learn.microsoft.com (tipos igualmente perigosos, alternativas preferidas, comportamento a partir do .NET 9, exemplos de cruzamento de fronteira de confiança)
  • Microsoft Learn, "SYSLIB0011" e "CA2326" — SYSLIB0011 / CA2326
  • OpenJDK, "JEP 290: Filter Incoming Serialization Data" e "JEP 415: Context-Specific Deserialization Filters" — JEP 290 / JEP 415
  • Documentação do Bandit (B301, B302, B506) — bandit.readthedocs.io
  • A tabela por linguagem, os passos de busca e os erros comuns foram conferidos com os documentos acima em 6 de outubro de 2026

Leia a seguir

FAQ

QPor que a desserialização é tão perigosa? Não é só conversão de dados?
A

Porque, em alguns formatos, não é conversão de dados, mas reconstrução de objetos. Quando a informação sobre qual classe instanciar viaja dentro dos dados, o atacante escolhe o que você constrói. A OWASP afirma que ataques contra desserializadores já permitiram negação de serviço, contorno de controle de acesso ou execução remota de código. O que torna isso difícil de defender é que o ataque dá certo antes de as suas verificações de valor rodarem.

QQual é a defesa mais confiável?
A

Restringir os dados de origem externa a um formato que não pode escolher tipos. A OWASP diz assim: ao trocar para um formato só de dados, como JSON ou XML, você reduz a chance de uma lógica de desserialização personalizada ser reaproveitada para fins maliciosos. Se um formato personalizado for inevitável, a mesma orientação observa que você pode assinar as mensagens como parte do processo de serialização e então optar por não desserializar nenhuma mensagem sem assinatura autenticada. Mude o formato primeiro; se não puder, amarre com uma assinatura.

QDá para torná-la segura reforçando a configuração?
A

Depende do mecanismo. A OWASP afirma claramente que o tipo BinaryFormatter do .NET é perigoso e não pode ser tornado seguro. Alguns mecanismos não ficam seguros com configurações ou validação extra — a única solução é parar de usá-los. Separe o que a configuração consegue proteger do que ela não consegue. Existem de fato ferramentas para as quais "está tudo bem se você usar corretamente" não vale.

QCom o que devo tomar cuidado na minha linguagem?
A

Entre o que a OWASP cita diretamente: em PHP, evite unserialize() e prefira JSON; em Python, pickle / c_pickle, o load do PyYAML e o jsonpickle são perigosos; em Java, sobrescreva ObjectInputStream#resolveClass() para restringir quais classes podem ser restauradas; em .NET, não use BinaryFormatter. O ponto em comum é que o mecanismo prático de persistência de uma linguagem é exatamente o que você não deve apontar para dados externos.

QComo encontro desserialização insegura no meu próprio código?
A

Em três passadas. Primeiro, faça uma busca de texto com ripgrep ou grep por chamadas como pickle.loads, unserialize, Marshal.load, ObjectInputStream e BinaryFormatter. Segundo, ative regras de análise estática (B301, B302 e B506 do Bandit para Python; as regras de análise de código da série CA2300 para .NET). Terceiro, verifique as suas dependências em busca de vulnerabilidades conhecidas com uma ferramenta como o osv-scanner e atualize primeiro as de CWE-502. Para cada chamada encontrada, anote de onde vêm os dados que ela recebe e corrija primeiro as que podem ser alcançadas de fora.

QUsar JSON é sempre seguro?
A

Não se você ativar uma configuração que deixa os dados nomearem os próprios tipos. O exemplo comum é o TypeNameHandling do Json.NET no .NET; a regra de análise de código CA2326 da Microsoft diz para não usar nenhum valor além de None. A vantagem do JSON é que ele carrega só valores, então o código que recebe decide qual classe é criada. Verifique se você não ativou uma configuração que elimina essa vantagem.