Compartir por


Habilitar la compatibilidad con el Portapapeles BinaryFormatter (no recomendado)

Precaución

BinaryFormatter No se recomienda la compatibilidad. Úselo solo como puente de migración temporal para aplicaciones heredadas que no pueden migrar inmediatamente a las nuevas API con seguridad de tipos. Este enfoque conlleva riesgos de seguridad significativos.

En este artículo se muestra cómo configurar la compatibilidad limitada BinaryFormatter con las operaciones del Portapapeles de Windows Forms en .NET 10. Aunque BinaryFormatter se quitó del entorno de ejecución en .NET 9 debido a vulnerabilidades de seguridad, restaure la funcionalidad limitada a través de la configuración explícita para las aplicaciones heredadas que necesitan tiempo de migración.

Para obtener instrucciones completas sobre la migración a las nuevas API seguras para tipos, consulte El Portapapeles de Windows Forms y los cambios de DataObject en .NET 10.

Importante

Este contenido solo se aplica a .NET moderno y no a .NET Framework, a menos que se especifique lo contrario.

Prerrequisitos

Antes de continuar, revise estos conceptos:

  • Cómo usa BinaryFormatter la aplicación actualmente en las operaciones del Portapapeles.
  • Vulnerabilidades de seguridad que llevaron a la eliminación de BinaryFormatter.
  • Escala de tiempo de migración a las nuevas API del Portapapeles con seguridad de tipos.

Para obtener más información, consulte estos artículos:

Advertencias y riesgos de seguridad

BinaryFormatter es intrínsecamente inseguro y en desuso por estas razones:

  • Vulnerabilidades arbitrarias de ejecución de código: los atacantes pueden ejecutar código malintencionado durante la deserialización, exponiendo la aplicación a ataques remotos.
  • Ataques por denegación de servicio: los datos malintencionados del Portapapeles pueden consumir recursos excesivos de memoria o CPU, lo que provoca bloqueos o inestabilidad.
  • Riesgos de divulgación de información: los atacantes pueden extraer datos confidenciales de la memoria.
  • Sin límites de seguridad: el formato es fundamentalmente no seguro y los valores de configuración no pueden protegerlo.

Habilite esta compatibilidad solo como puente temporal mientras actualiza la aplicación para usar las nuevas API seguras para tipos.

Instalación del paquete de compatibilidad

Agregue el paquete de compatibilidad no soportado BinaryFormatter a tu proyecto. Este paquete proporciona la compatibilidad necesaria en tiempo de ejecución para las operaciones BinaryFormatter.

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

Nota:

Este paquete está marcado como no admitido y en desuso. Úselo solo para la compatibilidad temporal durante la migración.

Habilitación de la serialización no segura en el proyecto

Establezca la EnableUnsafeBinaryFormatterSerialization propiedad true en el archivo del proyecto. Esta propiedad indica al compilador que permita el uso de BinaryFormatter.

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

Sin esta configuración, la aplicación genera errores de compilación cuando intenta usar BinaryFormatter las API.

Configurar el conmutador de tiempo de ejecución de Windows Forms

Cree o actualice el archivo runtimeconfig.json de la aplicación para habilitar el interruptor del portapapeles específico de Windows Forms. Esta configuración permite que las operaciones del portapapeles se reviertan a BinaryFormatter cuando sea necesario.

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

Importante

Sin este interruptor de tiempo de ejecución específico, las operaciones del portapapeles no regresarán a BinaryFormatter aunque la compatibilidad con la serialización general esté habilitada. Este interruptor es necesario para la funcionalidad del Portapapeles específicamente de Windows Forms y WPF.

Implementar solucionadores de tipos enfocados en la seguridad

Incluso con BinaryFormatter habilitado, implemente solucionadores de tipos para restringir la deserialización a tipos aprobados explícitamente. Los solucionadores de tipos proporcionan su única defensa contra ataques de carga malintencionada.

Crear un solucionador de tipos 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

Utilizar el resolutor de tipos con operaciones del portapapeles

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

Directrices de seguridad para solucionadores de tipos

Siga estas directrices de seguridad esenciales al implementar solucionadores de tipos:

Usar listas de permitidos explícitas

  • Rechazar de forma predeterminada: permitir solo tipos enumerados explícitamente.
  • Sin caracteres comodín: evite la coincidencia de patrones o los permisos basados en espacios de nombres.
  • Coincidencia exacta: requerir coincidencias exactas de cadena para los nombres de tipo.

Validar todas las entradas

  • Validación del nombre de tipo: asegúrese de que los nombres de tipo coinciden con los formatos esperados.
  • Restricciones de ensamblado: limite los tipos a ensamblados conocidos y de confianza.
  • Comprobación de versiones: considere la posibilidad de tener en cuenta las restricciones de tipo específicas de la versión.

Manejo de tipos desconocidos de manera segura

  • Lanzar excepciones: siempre lanzar para tipos no autorizados.
  • Intentos de registro: considere la posibilidad de registrar intentos de acceso no autorizados.
  • Mensajes claros de error: Proporcione motivos de rechazo específicos para la depuración.

Mantenimiento normal

  • Auditar periódicamente: revise y actualice la lista de tipos permitidos.
  • Quitar tipos sin usar: elimine los permisos de los tipos ya no necesarios.
  • Decisiones de documento: mantenga una documentación clara de por qué se permite cada tipo.

Prueba de la configuración

Después de configurar la BinaryFormatter compatibilidad, pruebe la aplicación para asegurarse de que funciona correctamente:

  1. Comprobar las operaciones del Portapapeles: pruebe el almacenamiento y la recuperación de datos con los tipos personalizados.
  2. Solucionador de tipos de prueba: confirme que los tipos no autorizados se rechazan correctamente.
  3. Supervisar la seguridad: Vigile si hay intentos inesperados de resolución de tipos.
  4. Pruebas de rendimiento: asegúrese de que la resolución de tipos no afecta significativamente al rendimiento.
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

Planeamiento de la estrategia de migración

Aunque el soporte BinaryFormatter ofrece compatibilidad temporal, desarrolle un plan de migración para pasar a las nuevas API seguras para tipos:

  1. Identificar el uso: cataloge todas las operaciones del Portapapeles mediante tipos personalizados.
  2. Priorizar la migración: céntrese primero en las operaciones más sensibles a la seguridad.
  3. Actualizar incrementalmente: migre una operación cada vez para reducir el riesgo.
  4. Prueba exhaustiva: asegúrese de que las nuevas implementaciones proporcionan una funcionalidad equivalente.
  5. Quitar BinaryFormatter: Deshabilite la compatibilidad una vez completada la migración.

Limpieza de recursos

Una vez que haya migrado a las nuevas API del portapapeles con tipado seguro, quite la BinaryFormatter configuración para mejorar la seguridad.

  1. Quite la referencia del System.Runtime.Serialization.Formatters paquete.
  2. Quite la EnableUnsafeBinaryFormatterSerialization propiedad de su archivo de proyecto.
  3. Quite la configuración Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization de su runtimeconfig.json.
  4. Elimine las implementaciones de resolución de tipos que ya no son necesarias.