FlawOpen

flawopen.com/Deserialización insegura/Seguridad de .NET BinaryFormatter

¿Es seguro BinaryFormatter en .NET?

CWE-502: Deserialization of Untrusted DataPágina de referencia
Respuesta breve: no existe una configuración segura

Microsoft afirma que BinaryFormatter es inseguro y no puede hacerse seguro. Recomienda dejar de usarlo incluso si la aplicación considera confiables los datos. En .NET 9, los tipos de API siguen presentes, pero la implementación integrada siempre lanza PlatformNotSupportedException; los runtimes antiguos y el paquete de compatibilidad sin soporte conservan el riesgo original.

INSEGURO
// No deserialices datos que crucen un límite de confianza.
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

public static class LegacyReader
{
    public static object Deserialize(Stream input) =>
        new BinaryFormatter().Deserialize(input);
}

// Elimina también estos serializadores polimórficos sin restricciones:
SoapFormatter
NetDataContractSerializer
LosFormatter
ObjectStateFormatter  // usado internamente por ViewState de ASP.NET

// SerializationBinder no hace seguros estos formatos.
MIGRAR
// El código de la aplicación declara la forma de los datos.
// Nullable reference types make the DTO contract explicit.
#nullable enable
using System;
using System.IO;
using System.Text.Json;

public sealed record MyDto(int Id, string Name);

public static class DtoReader
{
    public static MyDto Parse(ReadOnlySpan<byte> json)
    {
        // Limita el tamaño de entrada y valida las reglas de la aplicación.
        if (json.Length > 65_536)
            throw new InvalidDataException();

        MyDto? value = JsonSerializer.Deserialize<MyDto>(json);
        if (value is null || value.Id <= 0 ||
            string.IsNullOrWhiteSpace(value.Name) ||
            value.Name.Length > 100)
            throw new InvalidDataException();

        return value;
    }
}

// Para archivos NRBF antiguos, usa NrbfDecoder para inspeccionar
// registros y asignar los valores permitidos a un DTO.
// No reconstruyas tipos arbitrarios del runtime.

Por qué BinaryFormatter es peligroso

BinaryFormatter.Deserialize reconstruye grafos de objetos desde un flujo y puede permitir que datos controlados por un atacante influyan en el flujo de ejecución. Microsoft advierte de denegación de servicio, divulgación de información y ejecución remota de código. El problema central es la deserialización polimórfica sin restricciones: los datos serializados pueden influir en los tipos y estados de objeto que se procesan. Ninguna opción del formateador lo convierte en un analizador seguro para bytes no confiables.

El riesgo no se limita a endpoints HTTP públicos. Archivos, cachés, colas, registros de bases de datos, mensajes de clientes de escritorio y estado sincronizado en la nube pueden quedar bajo influencia de un atacante más adelante. Rastrea cómo entran y circulan los bytes; “lo escribimos nosotros” o “es interno” no es una etiqueta de confianza permanente.

Por qué SerializationBinder no lo soluciona

Un SerializationBinder personalizado puede restringir los tipos que el formateador intenta resolver, pero Microsoft indica expresamente que ni un binder ni otra opción de configuración mitigan la denegación de servicio y que pueden ser insuficientes frente a otras clases de ataque. El binder no sustituye la migración; no aceptes entradas no confiables mientras esperas a eliminar BinaryFormatter.

Encuentra usos antiguos y rutas relacionadas

Busca en el código fuente, las referencias de proyectos y la configuración del runtime: BinaryFormatter, IFormatter, SoapFormatter, NetDataContractSerializer, LosFormatter y ObjectStateFormatter. Revisa proveedores de sesión, adaptadores de caché, consumidores de colas, importación/exportación de archivos y aplicaciones antiguas de ASP.NET Web Forms. ViewState suele protegerse con un código de autenticación de mensajes (MAC); mantén esa protección y protege la machine key. Ese MAC no hace segura la deserialización genérica con BinaryFormatter.

Planifica la migración según formato y versión del runtime

Para datos nuevos, elige un contrato explícito como System.Text.Json, DataContractSerializer, MessagePack o protobuf-net según la compatibilidad y el formato; no existe un reemplazo directo. Para conservar datos NRBF antiguos, NrbfDecoder de .NET 9 puede decodificar registros sin instanciar los tipos codificados. Valida la estructura decodificada y asigna solo valores esperados a DTO de la aplicación. Revisa por separado las guías de migración de recursos, portapapeles y arrastrar y soltar de WinForms/WPF.

Preguntas frecuentes

Solo deserializamos datos que nosotros mismos escribimos. ¿Está bien?

Microsoft recomienda migrar incluso si los datos parecen confiables, porque el almacenamiento, las cachés, los archivos, la sincronización y los servicios posteriores pueden cruzar límites de confianza. Identifica quién puede escribir en cada origen y deja de usar BinaryFormatter en vez de confiar en esa suposición.

¿Qué ocurre al migrar a .NET 9?

En .NET 9, los tipos de API siguen existiendo, pero la implementación integrada siempre lanza PlatformNotSupportedException, independientemente del antiguo indicador de compatibilidad. Audita los usos y migra antes de actualizar. Un paquete de compatibilidad sin soporte restaura la implementación vulnerable y no soluciona la seguridad.

¿Json.NET es un reemplazo seguro?

De forma predeterminada, Json.NET establece TypeNameHandling en None, lo que evita que los metadatos del payload elijan tipos CLR arbitrarios. No habilites otros valores para entradas no confiables. Si necesitas polimorfismo, usa una lista permitida controlada por la aplicación y sigue la guía de la biblioteca. Consulta ¿Es seguro TypeNameHandling?

Fuentes y referencias

Véase también Deserialización insegura: riesgos y defensa ObjectInputStream en Java: deserialización segura Newtonsoft.Json TypeNameHandling: ¿configuración segura? PHP unserialize() y datos no confiables Seguridad de pickle.load() en Python: riesgos de datos no confiables ¿Es seguro Ruby YAML.load? Versiones de Psych y safe_load