Condividi tramite


Abilitare il supporto di BinaryFormatter per gli Appunti (non consigliato)

Attenzione

BinaryFormatter il supporto non è consigliato. Usarlo solo come ponte temporaneo di migrazione per le applicazioni legacy che non possono eseguire immediatamente la migrazione alle nuove API sicure per i tipi. Questo approccio comporta rischi significativi per la sicurezza.

Questo articolo illustra come configurare un supporto limitato BinaryFormatter per le operazioni degli Appunti di Windows Forms in .NET 10. Mentre BinaryFormatter è stato rimosso dal runtime in .NET 9 a causa di vulnerabilità di sicurezza, ripristinare funzionalità limitate tramite la configurazione esplicita per le applicazioni legacy che richiedono tempo per la migrazione.

Per indicazioni complete sulla migrazione alle nuove API a tipi sicuri, vedere le modifiche degli Appunti di Windows Forms e DataObject in .NET 10.

Importante

Questo contenuto si applica solo a .NET moderno e non a .NET Framework, se non diversamente specificato.

Prerequisiti

Prima di continuare, esaminare questi concetti:

  • Modalità con cui l'applicazione attualmente utilizza BinaryFormatter nelle operazioni negli Appunti.
  • Vulnerabilità di sicurezza che hanno portato alla rimozione di BinaryFormatter.
  • Pianificazione della migrazione alle nuove API degli appunti sicure di tipo.

Per altre informazioni, vedere questi articoli:

Avvisi e rischi per la sicurezza

BinaryFormatter è intrinsecamente non sicuro e deprecato per questi motivi:

  • Vulnerabilità di esecuzione di codice arbitrarie: gli utenti malintenzionati possono eseguire codice dannoso durante la deserializzazione, esponendo l'applicazione ad attacchi remoti.
  • Attacchi Denial of Service: i dati dannosi negli appunti possono utilizzare risorse di memoria o CPU in eccesso, causando arresti anomali o instabilità.
  • Rischi di divulgazione delle informazioni: gli utenti malintenzionati potrebbero estrarre dati sensibili dalla memoria.
  • Nessun limite di sicurezza: il formato è fondamentalmente non sicuro e le impostazioni di configurazione non possono proteggerlo.

Abilita questo supporto solo come ponte temporaneo mentre aggiorni l'applicazione per utilizzare le nuove API tipizzate.

Installare il pacchetto di compatibilità

Aggiungere il pacchetto di compatibilità non supportato BinaryFormatter al progetto. Questo pacchetto fornisce il supporto di runtime necessario per BinaryFormatter le operazioni:

<ItemGroup>
  <PackageReference Include="System.Runtime.Serialization.Formatters" Version="10.0.0*-*"/>
</ItemGroup>

Annotazioni

Questo pacchetto è contrassegnato come non supportato e deprecato. Usarlo solo per la compatibilità temporanea durante la migrazione.

Abilitare la serializzazione non sicura nel progetto

Impostare la EnableUnsafeBinaryFormatterSerialization proprietà su true nel file di progetto. Questa proprietà indica al compilatore di consentire l'utilizzo di BinaryFormatter

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
  <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>

Senza questa impostazione, l'applicazione genera errori di compilazione quando tenta di usare BinaryFormatter le API.

Configurare l'interruttore runtime di Windows Forms

Creare o aggiornare il file dell'applicazione runtimeconfig.json per abilitare l'opzione degli Appunti specifica di Windows Forms. Questa configurazione consente alle operazioni degli Appunti di eseguire il fallback a BinaryFormatter quando necessario:

{
  "runtimeOptions": {
    "configProperties": {
      "Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization": true
    }
  }
}

Importante

Senza questo commutatore di runtime specifico, le operazioni degli Appunti non torneranno a BinaryFormatter nemmeno se è abilitato il supporto della serializzazione generale. Questa opzione è necessaria specificamente per le funzionalità degli Appunti di Windows Forms e WPF.

Implementare resolver di tipo focalizzati sulla sicurezza

Anche quando BinaryFormatter è abilitato, implementare i resolver dei tipi per limitare la deserializzazione ai tipi approvati in modo esplicito. I resolver di tipi forniscono la sola difesa dagli attacchi di payload dannosi.

Creare un risolutore di tipi sicuro

// Create a security-focused type resolver
private static Type SecureTypeResolver(TypeName typeName)
{
    // Explicit allow-list of permitted types—add only what you need
    var allowedTypes = new Dictionary<string, Type>
    {
        ["MyApp.Person"] = typeof(Person),
        ["MyApp.AppSettings"] = typeof(AppSettings),
        ["System.String"] = typeof(string),
        ["System.Int32"] = typeof(int),
        // Add only the specific types your application requires
    };

    // Only allow explicitly listed types - exact string match required
    if (allowedTypes.TryGetValue(typeName.FullName, out Type allowedType))
    {
        return allowedType;
    }

    // Reject any type not in the allow-list with clear error message
    throw new InvalidOperationException(
        $"Type '{typeName.FullName}' is not permitted for clipboard deserialization");
}
' Create a security-focused type resolver
Private Shared Function SecureTypeResolver(typeName As TypeName) As Type
    ' Explicit allow-list of permitted types—add only what you need
    Dim allowedTypes As New Dictionary(Of String, Type) From {
        {"MyApp.Person", GetType(Person)},
        {"MyApp.AppSettings", GetType(AppSettings)},
        {"System.String", GetType(String)},
        {"System.Int32", GetType(Integer)}
    } ' Add only the specific types your application requires

    ' Only allow explicitly listed types - exact string match required
    Dim allowedType As Type = Nothing
    If allowedTypes.TryGetValue(typeName.FullName, allowedType) Then
        Return allowedType
    End If

    ' Reject any type not in the allow-list with clear error message
    Throw New InvalidOperationException(
        $"Type '{typeName.FullName}' is not permitted for clipboard deserialization")
End Function

Usare il sistema di risoluzione dei tipi con le operazioni degli Appunti

// Use the resolver with clipboard operations
private static Type SecureTypeResolver(TypeName typeName)
{
    // Implementation from SecureTypeResolver example
    // ... (allow-list implementation here)
    throw new InvalidOperationException($"Type '{typeName.FullName}' is not permitted");
}

public static void UseSecureTypeResolver()
{
    // Retrieve legacy data using the secure type resolver
    if (Clipboard.TryGetData("LegacyData", SecureTypeResolver, out MyCustomType data))
    {
        ProcessLegacyData(data);
    }
    else
    {
        Console.WriteLine("No compatible data found on clipboard");
    }
}
' Use the resolver with clipboard operations
Private Shared Function SecureTypeResolver(typeName As TypeName) As Type
    ' Implementation from SecureTypeResolver example
    ' ... (allow-list implementation here)
    Throw New InvalidOperationException($"Type '{typeName.FullName}' is not permitted")
End Function

Public Shared Sub UseSecureTypeResolver()
    ' Retrieve legacy data using the secure type resolver
    Dim data As MyCustomType = Nothing
    If Clipboard.TryGetData("LegacyData", AddressOf SecureTypeResolver, data) Then
        ProcessLegacyData(data)
    Else
        Console.WriteLine("No compatible data found on clipboard")
    End If
End Sub

Linee guida sulla sicurezza dei risolutori di tipi

Per l'implementazione dei resolver dei tipi, seguire queste linee guida di sicurezza essenziali:

Usare elenchi espliciti di autorizzazione

  • Rifiuta per impostazione predefinita: consente solo tipi elencati in modo esplicito.
  • Nessun carattere jolly: evita la corrispondenza di modelli o le autorizzazioni basate sullo spazio dei nomi.
  • Corrispondenza esatta: richiedi corrispondenze esatte di stringhe per i nomi dei tipi.

Convalidare tutti gli input

  • Convalida del nome del tipo: verificare che i nomi dei tipi corrispondano ai formati previsti.
  • Restrizioni dell'assembly: limita i tipi agli assembly noti e attendibili.
  • Controllo della versione: prendere in considerazione le restrizioni relative al tipo specifico della versione.

Gestire i tipi sconosciuti in modo sicuro

  • Genera eccezioni: genera sempre un'eccezione per i tipi non autorizzati.
  • Tentativi di log: prendere in considerazione la registrazione di tentativi di accesso non autorizzati.
  • Cancellare i messaggi di errore: fornire motivi di rifiuto specifici per il debug.

Manutenzione regolare

  • Controlla regolarmente: esaminare e aggiornare l'elenco dei tipi consentiti.
  • Rimuovere i tipi inutilizzati: eliminare le autorizzazioni per i tipi non più necessari.
  • Decisioni sui documenti: mantenere una documentazione chiara del motivo per cui è consentito ogni tipo.

Testare la configurazione

Dopo aver configurato il BinaryFormatter supporto, testare l'applicazione per assicurarsi che funzioni correttamente:

  1. Verificare le operazioni degli Appunti: testare sia l'archiviazione che il recupero dei dati con i tipi personalizzati.
  2. Risolutore tipo: verificare che i tipi non autorizzati siano adeguatamente rifiutati.
  3. Monitorare la sicurezza: Osservare eventuali tentativi imprevisti di risoluzione dei tipi.
  4. Test delle prestazioni: assicurarsi che il sistema di risoluzione dei tipi non influisca in modo significativo sulle prestazioni.
public static void TestBinaryFormatterConfiguration()
{
    // Test data to verify configuration
    var testPerson = new Person { Name = "Test User", Age = 30 };

    try
    {
        // Test storing data (this should work with proper configuration)
        Clipboard.SetData("TestPerson", testPerson);
        Console.WriteLine("Successfully stored test data on clipboard");

        // Test retrieving with type resolver
        if (Clipboard.TryGetData("TestPerson", SecureTypeResolver, out Person retrievedPerson))
        {
            Console.WriteLine($"Successfully retrieved: {retrievedPerson.Name}, Age: {retrievedPerson.Age}");
        }
        else
        {
            Console.WriteLine("Failed to retrieve test data");
        }

        // Test that unauthorized types are rejected
        try
        {
            Clipboard.TryGetData("TestPerson", UnauthorizedTypeResolver, out Person _);
            Console.WriteLine("ERROR: Unauthorized type was not rejected!");
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine($"SUCCESS: Unauthorized type properly rejected - {ex.Message}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Configuration test failed: {ex.Message}");
    }
}

private static Type SecureTypeResolver(TypeName typeName)
{
    var allowedTypes = new Dictionary<string, Type>
    {
        ["ClipboardExamples.Person"] = typeof(Person),
    };

    if (allowedTypes.TryGetValue(typeName.FullName, out Type allowedType))
    {
        return allowedType;
    }

    throw new InvalidOperationException($"Type '{typeName.FullName}' is not permitted");
}

private static Type UnauthorizedTypeResolver(TypeName typeName)
{
    // Intentionally restrictive resolver to test rejection
    throw new InvalidOperationException($"No types are permitted by this test resolver");
}
Public Shared Sub TestBinaryFormatterConfiguration()
    ' Test data to verify configuration
    Dim testPerson As New Person With {.Name = "Test User", .Age = 30}

    Try
        ' Test storing data (this should work with proper configuration)
        Clipboard.SetData("TestPerson", testPerson)
        Console.WriteLine("Successfully stored test data on clipboard")

        ' Test retrieving with type resolver
        Dim retrievedPerson As Person = Nothing
        If Clipboard.TryGetData("TestPerson", AddressOf SecureTypeResolver, retrievedPerson) Then
            Console.WriteLine($"Successfully retrieved: {retrievedPerson.Name}, Age: {retrievedPerson.Age}")
        Else
            Console.WriteLine("Failed to retrieve test data")
        End If

        ' Test that unauthorized types are rejected
        Try
            Dim testResult As Person = Nothing
            Clipboard.TryGetData("TestPerson", AddressOf UnauthorizedTypeResolver, testResult)
            Console.WriteLine("ERROR: Unauthorized type was not rejected!")
        Catch ex As InvalidOperationException
            Console.WriteLine($"SUCCESS: Unauthorized type properly rejected - {ex.Message}")
        End Try

    Catch ex As Exception
        Console.WriteLine($"Configuration test failed: {ex.Message}")
    End Try
End Sub

Private Shared Function SecureTypeResolver(typeName As TypeName) As Type
    Dim allowedTypes As New Dictionary(Of String, Type) From {
        {"ClipboardExamples.Person", GetType(Person)}
    }

    Dim allowedType As Type = Nothing
    If allowedTypes.TryGetValue(typeName.FullName, allowedType) Then
        Return allowedType
    End If

    Throw New InvalidOperationException($"Type '{typeName.FullName}' is not permitted")
End Function

Private Shared Function UnauthorizedTypeResolver(typeName As TypeName) As Type
    ' Intentionally restrictive resolver to test rejection
    Throw New InvalidOperationException($"No types are permitted by this test resolver")
End Function

Pianificare la strategia di migrazione

Il supporto di BinaryFormatter fornisce compatibilità temporanea, quindi sviluppare un piano di migrazione per passare alle nuove API a prova di tipo.

  1. Identificare l'utilizzo: registrare tutte le operazioni degli Appunti usando tipi definiti dall'utente.
  2. Assegnare priorità alla migrazione: concentrarsi prima sulle operazioni più sensibili alla sicurezza.
  3. Aggiornamento incrementale: eseguire la migrazione di un'operazione alla volta per ridurre i rischi.
  4. Test approfondito: assicurarsi che le nuove implementazioni forniscano funzionalità equivalenti.
  5. Rimuovi BinaryFormatter: disabilitare il supporto al termine della migrazione.

Pulire le risorse

Dopo aver eseguito la migrazione alle nuove API degli Appunti a tipi sicuri, rimuovere la BinaryFormatter configurazione per migliorare la sicurezza.

  1. Rimuovere il riferimento al System.Runtime.Serialization.Formatters pacchetto.
  2. Rimuovere la EnableUnsafeBinaryFormatterSerialization proprietà dal file di progetto.
  3. Rimuovi l'impostazione Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization da runtimeconfig.json.
  4. Eliminare le implementazioni del risolutore di tipi che non sono più necessarie.