FlawOpen

flawopen.com/Desserialização insegura/PHP unserialize()

unserialize() do PHP é seguro para entrada não confiável?

CWE-502: Deserialization of Untrusted DataPágina de referência
Resposta curta

Não passe entrada não confiável a unserialize(), mesmo ao definir allowed_classes. O PHP pode restaurar objetos e chamar __unserialize() ou __wakeup(); um atacante pode explorar classes disponíveis à aplicação ou ao autoloader. Use JSON para dados não confiáveis. Com decodificação associativa, objetos JSON viram arrays e não instanciam classes da aplicação com comportamento de métodos mágicos.

VULNERÁVEL
<?php
// Cookie content is controlled by the client.
$input = $_COOKIE['prefs'] ?? '';
$data = unserialize($input);

// Restoring an object may invoke its
// __unserialize() or __wakeup() method.
// Later object destruction may invoke __destruct().

// The risk depends on available classes,
// their serialized state, and reachable behavior.
CORRIGIDO
<?php
$input = $_POST['prefs'] ?? null;
if (!is_string($input)) {
    http_response_code(400);
    exit;
}

try {
    $data = json_decode(
        $input,
        true,
        512,
        JSON_THROW_ON_ERROR
    );
} catch (JsonException $e) {
    http_response_code(400);
    exit;
}

if (!is_array($data)) {
    http_response_code(400);
    exit;
}

// Validate required keys and value types here.

O que uma cadeia de injeção de objetos PHP exige

O risco de injeção de objetos não significa execução automática de código. O valor serializado precisa nomear uma classe que o PHP consiga resolver, permitida pelas opções de desserialização; a restauração ou o uso posterior também deve alcançar um comportamento com impacto de segurança. O PHP pode chamar __unserialize() ou __wakeup() ao restaurar o objeto; __destruct() pode rodar quando ele for destruído, e __toString() apenas quando usado em contexto de string.

Uma cadeia Property-Oriented Programming (POP) liga comportamentos já presentes na aplicação ou nas dependências carregadas. A explorabilidade depende das classes reais, das propriedades serializadas, do caminho de chamadas e das condições de execução. É preciso demonstrar uma cadeia de gadgets para o alvo; a presença de unserialize() por si só não a comprova.

Por que filtrar a string ou usar allowed_classes não é correção completa

Bloquear um marcador como O:, filtrar nomes de classes ou limitar o tamanho da string não valida com segurança a serialização PHP. Mais importante: o PHP alerta explicitamente para não passar entrada não confiável a unserialize(), independentemente de allowed_classes. Se uma classe não for permitida, o PHP cria um objeto __PHP_Incomplete_Class; a estrutura continua sendo analisada. Para dados legados confiáveis, valide a forma resultante e limite a profundidade. Prefira JSON para estado vindo do cliente.

Perguntas frequentes

allowed_classes => false torna segura a entrada não confiável?

Não. A opção impede a restauração normal de classes da aplicação; objetos não permitidos viram __PHP_Incomplete_Class. O PHP ainda alerta para não passar dados não confiáveis a unserialize(), mesmo com essa opção. Use-a apenas como defesa em profundidade para dados legados confiáveis, valide o resultado e defina max_depth limitado. O manual do PHP informa que esta opção não afeta enums.

Um HMAC torna os dados serializados seguros?

Pode comprovar a integridade de dados gerados pela aplicação se a chave permanecer secreta e hash_equals() validar a assinatura antes da desserialização. Rejeite assinaturas inválidas antes de analisar os dados. HMAC não cifra nem impede repetição; use expiração ou nonce quando for necessário garantir atualidade. Não use assinatura para legitimar dados serializados arbitrários do usuário.

Este é o mesmo risco de Python pickle ou Java ObjectInputStream?

Eles compartilham o risco amplo de restaurar objetos de dados serializados, mas formatos, hooks, regras de resolução de classes e mitigação diferem. Avalie cada runtime e suas classes disponíveis separadamente; não presuma que uma cadeia de gadgets de uma plataforma funcione em outra.

Referências

Veja também Desserialização insegura: riscos e correção Segurança do pickle.load() em Python: riscos de dados não confiáveis Ruby YAML.load é seguro? Versões do Psych e safe_load .NET BinaryFormatter: riscos e migração ObjectInputStream Java: desserialização segura Newtonsoft.Json TypeNameHandling: configuração segura?