Partilhar via


Ativar o suporte à área de transferência BinaryFormatter (não recomendado)

Atenção

BinaryFormatter O suporte não é recomendado. Use-o apenas como uma ponte de migração temporária para aplicações legadas que não podem migrar imediatamente para as novas APIs seguras em termos de tipo. Esta abordagem comporta riscos de segurança significativos.

Este artigo mostra como configurar um suporte limitado BinaryFormatter para operações da área de transferência do Windows Forms no .NET 10. Embora BinaryFormatter tenha sido removido do tempo de execução no .NET 9 devido a vulnerabilidades de segurança, restaure a funcionalidade limitada por meio de configuração explícita para aplicativos herdados que precisam de tempo para migrar.

Para obter orientações completas para migração para as novas APIs com segurança de tipos, consulte Área de transferência do Windows Forms e alterações no DataObject no .NET 10.

Importante

Este conteúdo aplica-se apenas ao .NET moderno e não ao .NET Framework, salvo indicação em contrário.

Pré-requisitos

Antes de continuar, analise estes conceitos:

  • Como é que o seu aplicativo usa BinaryFormatter atualmente em operações da área de transferência.
  • As vulnerabilidades de segurança que levaram à remoção do BinaryFormatter.
  • O seu cronograma de migração para as novas APIs de transferência segura de tipos.

Para obter mais informações, consulte estes artigos:

Avisos e riscos de segurança

BinaryFormatter é inerentemente insegura e preterida pelos seguintes motivos:

  • Vulnerabilidades de execução arbitrária de código: os invasores podem executar código mal-intencionado durante a desserialização, expondo seu aplicativo a ataques remotos.
  • Ataques de negação de serviço: dados mal-intencionados da área de transferência podem consumir em excesso memória ou recursos de CPU, causando falhas no sistema ou instabilidade.
  • Riscos de divulgação de informações: os invasores podem extrair dados confidenciais da memória.
  • Sem limites de segurança: o formato é fundamentalmente inseguro e as definições de configuração não podem protegê-lo.

Habilite esse suporte apenas como uma ponte temporária enquanto atualiza seu aplicativo para usar as novas APIs seguras para tipos.

Instalar o pacote de compatibilidade

Adicione o pacote de compatibilidade não suportado BinaryFormatter ao seu projeto. Este pacote fornece o suporte de tempo de execução necessário para BinaryFormatter operações:

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

Observação

Este pacote está marcado como não suportado e preterido. Use-o apenas para compatibilidade temporária durante a migração.

Habilitar a serialização insegura em seu projeto

Defina a EnableUnsafeBinaryFormatterSerialization propriedade como true em seu arquivo de projeto. Esta propriedade instrui o compilador a permitir o uso de BinaryFormatter.

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

Sem essa configuração, seu aplicativo gera erros de compilação quando tenta usar BinaryFormatter APIs.

Configurar a opção de tempo de execução do Windows Forms

Crie ou atualize o arquivo runtimeconfig.json do seu aplicativo para habilitar a opção de área de transferência específica para o Windows Forms. Esta configuração permite que as operações da área de transferência recaiam para BinaryFormatter quando necessário:

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

Importante

Sem essa opção específica de tempo de execução, as operações da área de transferência não terão como alternativa o recurso BinaryFormatter, mesmo que o suporte geral à serialização esteja habilitado. Essa opção é necessária especificamente para a funcionalidade do Windows Forms e da área de transferência do WPF.

Implementar resolvedores de tipo focados na segurança

Mesmo com BinaryFormatter habilitado, implemente resolvedores de tipo para restringir a desserialização a tipos explicitamente aprovados. Os resolvedores de tipos fornecem sua única defesa contra ataques mal-intencionados de carga útil.

Criar um resolvedor de tipo seguro

// 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

Usar o resolvedor de tipos com operações da área de transferência

// 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

Diretrizes de segurança para resolvedores de tipo

Siga estas diretrizes de segurança essenciais ao implementar resolvedores de tipo:

Usar listas de permissões explícitas

  • Rejeitar por padrão: permita apenas tipos explicitamente listados.
  • Sem curingas: evite correspondência de padrões ou permissões baseadas em namespace.
  • Correspondência exata: requira correspondências exatas de cadeias de caracteres para nomes de tipos.

Validar todas as entradas

  • Validação do nome do tipo: certifique-se de que os nomes dos tipos correspondam aos formatos esperados.
  • Restrições de assembly: Limite tipos a assemblies que sejam conhecidos e confiáveis.
  • Verificação de versão: considere restrições de tipo específicas da versão.

Lide com tipos desconhecidos com segurança

  • Lançar exceções: sempre lance para tipos não autorizados.
  • Registrar tentativas: Considere registrar tentativas de acesso não autorizado.
  • Tornar claras as mensagens de erro: ofereça motivos específicos de rejeição para depuração.

Manutenção regular

  • Auditar regularmente: revise e atualize a lista de tipos permitidos.
  • Remover tipos não utilizados: elimine permissões para tipos que não são mais necessários.
  • Decisões documentais: Mantenha uma documentação clara do motivo pelo qual cada tipo é permitido.

Teste sua configuração

Depois de configurar BinaryFormatter o suporte, teste seu aplicativo para garantir que ele funcione corretamente:

  1. Verificar as operações da área de transferência: teste o armazenamento e a recuperação de dados com seus tipos personalizados.
  2. Resolvedor de tipos de teste: confirme se os tipos não autorizados foram rejeitados corretamente.
  3. Monitorize a segurança: esteja atento a quaisquer tentativas inesperadas de resolução de tipos.
  4. Teste de desempenho: certifique-se de que o resolvedor de tipo não afeta significativamente o desempenho.
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

Planeie a sua estratégia de migração

Embora BinaryFormatter o suporte forneça compatibilidade temporária, desenvolva um plano de migração para migrar para as novas APIs seguras para tipos:

  1. Identificar uso: catalogar todas as operações da área de transferência usando tipos personalizados.
  2. Priorize a migração: concentre-se primeiro nas operações mais sensíveis à segurança.
  3. Atualizar incrementalmente: migre uma operação de cada vez para reduzir o risco.
  4. Teste minuciosamente: Garanta que novas implementações forneçam funcionalidade equivalente.
  5. Remover BinaryFormatter: desative o suporte assim que a migração for concluída.

Limpeza de recursos

Depois de migrar para as novas APIs de área de transferência seguras, do tipo seguro, remova a configuração BinaryFormatter para melhorar a segurança.

  1. Remova a referência de pacote System.Runtime.Serialization.Formatters.
  2. Remova a EnableUnsafeBinaryFormatterSerialization propriedade do arquivo de projeto.
  3. Remova a definição Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization do seu runtimeconfig.json.
  4. Exclua implementações de resolvedor de tipo que não são mais necessárias.