# Desserialização insegura em PHP: unserialize e segurança

> Usar o unserialize em PHP com segurança: porque são perigosos os objetos vindos de entradas de utilizadores, como o JSON, o allowed_classes e as assinaturas protegem.

Fonte: https://phpsos.de/pt/ratgeber/deserialisierung-php/  
Atualizado em: 25/10/2026  
Prestador: PHP SOS, Assistência de emergência PHP e segurança de servidores. Ajuda imediata por WhatsApp: https://wa.me/491736494914?text=Ol%C3%A1%2C%20PHP%20SOS%2C%20precisamos%20de%20ajuda%20com%20o%20nosso%20servidor%20PHP.%20Empresa%3A%20%E2%80%A6%20Site%3A%20%E2%80%A6

**Em resumo:** na segurança do unserialize em PHP aplica-se uma regra simples: nunca processar com `unserialize()` dados que vêm do exterior. Utilize antes JSON. Onde o `unserialize()` tiver de permanecer por código antigo, limite-o com `allowed_classes => false` e verifique a autenticidade dos dados com uma assinatura.

## O que pode correr mal com o unserialize()

O PHP pode converter arrays e objetos numa cadeia de caracteres com `serialize()` e restaurá-los com `unserialize()`. É cómodo para guardar dados em cookies, caches ou campos de bases de dados. O problema: o `unserialize()` cria também objetos de classes arbitrárias que possam ser carregadas na aplicação.

Ao criar e, o mais tardar, ao destruir um objeto desses, o PHP chama automaticamente determinados métodos, como `__wakeup()`, `__unserialize()` ou `__destruct()`. Se classes do projeto ou de bibliotecas tiverem tais métodos com ações sobre ficheiros, bases de dados ou o sistema, um atacante pode desviá-los do seu fim através de dados manipulados. Os especialistas falam em PHP Object Injection. Consoante as classes existentes, o efeito vai da eliminação de ficheiros isolados à execução de código malicioso.

Resumido para não técnicos: uma única chamada irrefletida no sítio errado pode deixar um atacante entrar fundo na aplicação. Esta fragilidade faz por isso parte há anos dos temas do OWASP Top 10.

## A alternativa segura: JSON em vez de unserialize

Em quase todos os casos em que os dados vêm do exterior ou saem do servidor, o JSON é a escolha certa. O `json_decode()` cria apenas arrays, números, cadeias de caracteres e valores booleanos, não objetos de classes próprias.

```php
<?php
declare(strict_types=1);

function leseEinstellungen(string $eingabe): array
{
    try {
        $daten = json_decode($eingabe, true, 16, JSON_THROW_ON_ERROR);
    } catch (JsonException $e) {
        throw new InvalidArgumentException('Ungültige Daten.');
    }

    if (!is_array($daten)) {
        throw new InvalidArgumentException('Erwartet wurde ein Objekt.');
    }

    // Aceitar apenas campos conhecidos com os tipos esperados
    return [
        'sprache' => in_array($daten['sprache'] ?? '', ['de', 'en'], true) ? $daten['sprache'] : 'de',
        'seiten'  => max(1, min(100, (int) ($daten['seiten'] ?? 20))),
    ];
}
```

Importante é a segunda parte: também os dados JSON são entradas. Aceite apenas os campos esperados e verifique tipos e intervalos de valores antes de trabalhar com eles.

## Quando o unserialize é inevitável: allowed_classes e assinatura

Em aplicações mais antigas, os dados serializados estão muitas vezes em bases de dados ou caches que não se podem converter de imediato. Limite então o risco em dois passos. Primeiro, impeça com `allowed_classes` que sejam criados objetos. Segundo, garanta com uma assinatura HMAC que os dados provêm realmente da sua aplicação e não foram alterados pelo caminho.

```php
<?php
declare(strict_types=1);

const SCHLUESSEL = 'aus-sicherer-konfiguration-laden'; // nunca no repositório de código

function verpacken(array $daten): string
{
    $roh = serialize($daten);
    return hash_hmac('sha256', $roh, SCHLUESSEL) . '.' . base64_encode($roh);
}

function auspacken(string $paket): array
{
    [$signatur, $b64] = array_pad(explode('.', $paket, 2), 2, '');
    $roh = base64_decode($b64, true);

    if ($roh === false || !hash_equals(hash_hmac('sha256', $roh, SCHLUESSEL), $signatur)) {
        throw new RuntimeException('Signatur ungültig.');
    }

    $daten = unserialize($roh, ['allowed_classes' => false, 'max_depth' => 16]);
    if (!is_array($daten)) {
        throw new RuntimeException('Unerwartetes Format.');
    }
    return $daten;
}
```

Se forem mesmo necessárias determinadas classes, indique-as uma a uma em `allowed_classes`, em vez de permitir todas. A chave pertence à configuração do servidor ou a um armazém de segredos e tem de ser renovada após um incidente.

### Converter gradualmente os dados existentes para JSON

Em aplicações crescidas, os valores serializados ficam muitas vezes anos na base de dados. Uma conversão num só passo é então arriscada. Uma transição tem-se revelado eficaz: a aplicação lê ambos os formatos, mas só escreve JSON. Ao ler um valor antigo, este é desembalado uma única vez com `allowed_classes => false` e imediatamente gravado de novo como JSON. Passado algum tempo, a maioria das entradas está convertida, e um script único trata do resto.

Durante a transição, registe quantas vezes ainda é lido o formato antigo e também quando as assinaturas não coincidem. Tais falhas podem indicar tentativas de manipulação e pertencem à monitorização. Só quando já não aparecer nenhum formato antigo é que remove por completo o ramo do `unserialize()` do código.

## Desserialização escondida: sessões, caches e caminhos de ficheiros

Nem toda a desserialização está visível no código próprio. Verifique também estes pontos:

- **Sessões:** o PHP serializa automaticamente os dados de sessão. Enquanto os atacantes não puderem escrever nos ficheiros de sessão, isso não é crítico. Caminhos de sessão separados por site são descritos no guia [Proteger sessões em PHP](https://phpsos.de/pt/ratgeber/session-sicherheit-php/).
- **Serializador de sessões:** o aviso sobre o [CVE-2026-115560](https://phpsos.de/pt/php2shell/cve-2026-115560/) diz respeito a uma configuração invulgar, em que o `serialize_handler` é alterado em tempo de execução por `ini_set()` e o session upload progress está ativo. Mitigação segundo o php.net: `session.upload_progress.enabled=0` no php.ini ou no pool FPM e, depois, reiniciar os workers.
- **Caches e filas de espera:** Redis, Memcached ou tabelas de bases de dados com valores serializados são tão seguros quanto o acesso a eles. Não pertencem abertos à rede.
- **Funções de ficheiros com caminhos vindos de entradas:** nunca passe às funções de ficheiros caminhos vindos do pedido. Os stream wrappers como `phar://` podem, de outro modo, desencadear um processamento inesperado. Uma lista de permissão para nomes de ficheiros protege aqui com mais fiabilidade do que qualquer filtragem.
- **Dependências:** as bibliotecas com fragilidades conhecidas ampliam as possibilidades de um atacante. Mantenha-as atualizadas, como descrito no guia [Verificar vulnerabilidades nas dependências do Composer](https://phpsos.de/pt/ratgeber/composer-abhaengigkeiten-pruefen/).

## Encontrar pontos arriscados no código

Um primeiro inventário consegue-se com uma pesquisa simples. Não altera nada no sistema e mostra todas as chamadas fora do diretório `vendor`:

```bash
grep -rn --include='*.php' 'unserialize(' /var/www/projekt \
  | grep -v '/vendor/'
```

Avalie cada resultado com três perguntas: de onde vêm os dados? Pode um utilizador alterá-los? O `allowed_classes` está definido? Os resultados com entradas de cookies, `$_GET`, `$_POST`, `$_REQUEST` ou interfaces externas têm prioridade. Substitua-os por JSON e teste a conversão antes de a pôr em produção.

Outro indício está no navegador. Os dados PHP serializados têm uma estrutura típica, com identificadores de tipo e indicações de comprimento. Se encontrar tais valores em cookies, campos de formulário ocultos ou parâmetros do seu próprio site, a aplicação entregou dados serializados ao visitante e espera provavelmente recebê-los de volta. São precisamente estes pontos que devem ser verificados.

Para a gestão basta uma pergunta simples aos programadores ou à agência: “A nossa aplicação processa em algum lado dados serializados que vêm do exterior e, se sim, como estão protegidos?” Uma resposta clara, com locais concretos, é um bom sinal. Uma resposta evasiva é motivo para mandar verificar a aplicação.

## Como ajuda concretamente a PHP SOS?

- Pesquisamos no seu código a desserialização com dados alheios e avaliamos cada achado segundo a acessibilidade e o impacto.
- Propomos conversões concretas para JSON ou chamadas protegidas e acompanhamos os seus programadores na implementação.
- Verificamos as dependências quanto a fragilidades conhecidas e documentamos o estado.
- Se desejar, mandamos verificar a aplicação de forma independente no âmbito de uma [auditoria de segurança e teste de intrusão](https://phpsos.de/pt/leistungen/sicherheitsaudit-penetrationstest/).


## Perguntas frequentes

**O unserialize() em PHP é inseguro por princípio?**  
Não para dados que só a sua própria aplicação gera e que ninguém pode alterar. Torna-se perigoso assim que utilizadores ou terceiros podem influenciar a entrada, por exemplo através de cookies, campos de formulário ou interfaces externas.

**O allowed_classes => false basta como proteção?**  
Impede que sejam criados objetos arbitrários e fecha, assim, o caminho de ataque mais importante. Para entradas vindas do exterior, o JSON continua a ser a melhor escolha, porque o analisador é mais simples e não reconstrói objetos.

**Atualizar bibliotecas ajuda contra ataques de desserialização?**  
Em parte. Os ataques usam muitas vezes classes de dependências que executam ações ao destruir ou ao acordar um objeto. Pacotes atuais fecham cadeias conhecidas, mas a verdadeira causa é a chamada ao unserialize com dados alheios.

**O PHP2Shell tem alguma relação com a desserialização?**  
As duas vulnerabilidades principais estão no processamento de pedidos do PHP. O aviso sobre o CVE-2026-115560 diz, em contrapartida, respeito a uma configuração invulgar da serialização de sessões. A mitigação aí é session.upload_progress.enabled=0.

## Fontes

- [OWASP Cheat Sheet Series (Deserialization)](https://cheatsheetseries.owasp.org/)
- [OWASP Top 10](https://owasp.org/www-project-top-ten/)
- [php.net: avisos de segurança](https://www.php.net/)
