FlawOpen

flawopen.com/Désérialisation non sécurisée/Sécurité de .NET BinaryFormatter

BinaryFormatter est-il sûr dans .NET ?

CWE-502: Deserialization of Untrusted DataPage de référence
Réponse courte : aucune configuration sûre

Microsoft indique que BinaryFormatter est dangereux et ne peut pas être sécurisé. Elle recommande d’arrêter son usage même si l’application estime les données fiables. Dans .NET 9, les types d’API existent toujours, mais l’implémentation intégrée lève toujours PlatformNotSupportedException ; les anciens runtimes et le package de compatibilité non pris en charge conservent le risque initial.

DANGEREUX
// Ne désérialisez pas de données qui franchissent une frontière de confiance.
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

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

// Supprimez aussi ces formateurs polymorphes sans restriction :
SoapFormatter
NetDataContractSerializer
LosFormatter
ObjectStateFormatter  // utilisé en interne par ViewState ASP.NET

// SerializationBinder ne sécurise pas ces formats.
MIGRER
// La forme des données est déclarée par le code applicatif.
// 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)
    {
        // Limitez la taille et validez les règles métier.
        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;
    }
}

// Pour les anciens fichiers NRBF, inspectez avec NrbfDecoder
// les enregistrements puis mappez les valeurs permises en DTO.
// Ne recréez jamais des types runtime arbitraires.

Pourquoi BinaryFormatter est dangereux

BinaryFormatter.Deserialize reconstruit des graphes d’objets depuis un flux et peut permettre à des données contrôlées par un attaquant d’influencer le flot d’exécution. Microsoft avertit des risques de déni de service, de divulgation d’informations et d’exécution de code à distance. Le problème central est la désérialisation polymorphe sans restriction : les données sérialisées peuvent influencer les types et l’état des objets traités. Aucun paramètre du formateur ne transforme cette API en analyseur sûr pour des octets non fiables.

Le risque ne concerne pas seulement les endpoints HTTP publics. Fichiers, caches, files de messages, enregistrements de base de données, messages de clients de bureau et état synchronisé dans le cloud peuvent devenir influençables par un attaquant. Suivez le chemin des octets dans le système : “nous l’avons écrit” ou “c’est interne” n’est pas une étiquette de confiance permanente.

Pourquoi SerializationBinder ne suffit pas

Un SerializationBinder personnalisé peut restreindre les types que le formateur tente de résoudre, mais Microsoft indique explicitement qu’un binder ou un autre paramètre ne peut pas atténuer le déni de service et peut être insuffisant contre d’autres attaques. Un binder ne remplace pas la migration ; n’acceptez pas d’entrée non fiable en attendant de supprimer BinaryFormatter.

Repérer les usages anciens et chemins associés

Recherchez dans le code, les références de projet et la configuration du runtime : BinaryFormatter, IFormatter, SoapFormatter, NetDataContractSerializer, LosFormatter et ObjectStateFormatter. Examinez les fournisseurs de session, adaptateurs de cache, consommateurs de files, imports/exports de fichiers et anciennes applications ASP.NET Web Forms. ViewState est généralement protégé par un code d’authentification de message (MAC) ; gardez cette protection et protégez la machine key. Ce MAC ne sécurise pas la désérialisation générale par BinaryFormatter.

Planifier la migration selon le format et la version du runtime

Pour les nouvelles données, choisissez un contrat explicite comme System.Text.Json, DataContractSerializer, MessagePack ou protobuf-net selon les besoins de format et de compatibilité ; il n’existe pas de remplacement direct. Pour conserver d’anciens fichiers NRBF, NrbfDecoder de .NET 9 peut décoder les enregistrements sans instancier les types encodés. Validez la structure décodée et mappez uniquement les valeurs attendues vers des DTO applicatifs. Consultez séparément les guides de migration WinForms/WPF pour ressources, presse-papiers et glisser-déposer.

FAQ

Nous ne désérialisons que des données écrites par nous. Est-ce acceptable ?

Microsoft recommande la migration même si les données semblent fiables : stockage, caches, fichiers, synchronisation ou services en aval peuvent franchir une frontière de confiance. Identifiez qui peut écrire chaque source et abandonnez BinaryFormatter plutôt que de vous fier à cette hypothèse.

Que se passe-t-il lors du passage à .NET 9 ?

Dans .NET 9, les types d’API existent toujours, mais l’implémentation intégrée lève toujours PlatformNotSupportedException, quel que soit l’ancien indicateur de compatibilité. Auditez les usages et migrez avant la mise à niveau. Un package de compatibilité non pris en charge restaure l’implémentation vulnérable ; ce n’est pas une correction de sécurité.

Json.NET est-il un remplacement sûr ?

Par défaut, Json.NET définit TypeNameHandling sur None, ce qui empêche les métadonnées du payload de choisir des types CLR arbitraires. N’activez pas d’autres valeurs pour des entrées non fiables. Si le polymorphisme est nécessaire, imposez une liste autorisée contrôlée par l’application et suivez les recommandations de la bibliothèque. Voir TypeNameHandling est-il sûr ?

Sources et références

À voir aussi Désérialisation non sécurisée : risques et défense Sécurité d’ObjectInputStream en Java : désérialisation sûre Newtonsoft.Json TypeNameHandling : configuration sûre ? PHP unserialize() et les données non fiables Python pickle.load() : risques des données non fiables Ruby YAML.load est-il sûr ? Versions de Psych et safe_load