Note
Access to this page requires authorization. You can try signing in or changing directories.
Access to this page requires authorization. You can try changing directories.
Caution
BinaryFormatter support isn't recommended. Use it only as a temporary migration bridge for legacy applications that can't immediately migrate to the new type-safe APIs. This approach carries significant security risks.
This article shows how to configure limited BinaryFormatter support for Windows Forms clipboard operations in .NET 10. While BinaryFormatter was removed from the runtime in .NET 9 due to security vulnerabilities, restore limited functionality through explicit configuration for legacy applications that need time to migrate.
For complete migration guidance to the new type-safe APIs, see Windows Forms clipboard and DataObject changes in .NET 10.
Important
This content only applies to modern .NET and not .NET Framework, unless otherwise specified.
Prerequisites
Before continuing, review these concepts:
- How your application currently uses
BinaryFormatterin clipboard operations. - The security vulnerabilities that led to the removal of
BinaryFormatter. - Your migration timeline to the new type-safe clipboard APIs.
For more information, see these articles:
Security warnings and risks
BinaryFormatter is inherently insecure and deprecated for these reasons:
- Arbitrary code execution vulnerabilities: Attackers can execute malicious code during deserialization, exposing your application to remote attacks.
- Denial of service attacks: Malicious clipboard data can consume excessive memory or CPU resources, causing crashes or instability.
- Information disclosure risks: Attackers might extract sensitive data from memory.
- No security boundaries: The format is fundamentally unsafe, and configuration settings can't secure it.
Enable this support only as a temporary bridge while you update your application to use the new type-safe APIs.
Install the compatibility package
Add the unsupported BinaryFormatter compatibility package to your project. This package provides the necessary runtime support for BinaryFormatter operations:
<ItemGroup>
<PackageReference Include="System.Runtime.Serialization.Formatters" Version="10.0.0*-*"/>
</ItemGroup>
Note
This package is marked as unsupported and deprecated. Use it only for temporary compatibility during migration.
Enable unsafe serialization in your project
Set the EnableUnsafeBinaryFormatterSerialization property to true in your project file. This property tells the compiler to allow BinaryFormatter usage:
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
Without this setting, your application generates compilation errors when it attempts to use BinaryFormatter APIs.
Configure the Windows Forms runtime switch
Create or update your application's runtimeconfig.json file to enable the Windows Forms-specific clipboard switch. This configuration allows clipboard operations to fall back to BinaryFormatter when necessary:
{
"runtimeOptions": {
"configProperties": {
"Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization": true
}
}
}
Important
Without this specific runtime switch, clipboard operations won't fall back to BinaryFormatter even if general serialization support is enabled. This switch is required specifically for Windows Forms and WPF clipboard functionality.
Implement security-focused type resolvers
Even with BinaryFormatter enabled, implement type resolvers to restrict deserialization to explicitly approved types. Type resolvers provide your only defense against malicious payload attacks.
Create a secure type resolver
// 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
Use the type resolver with clipboard operations
// 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
Security guidelines for type resolvers
Follow these essential security guidelines when implementing type resolvers:
Use explicit allowlists
- Reject by default: Allow only explicitly listed types.
- No wildcards: Avoid pattern matching or namespace-based permissions.
- Exact matching: Require exact string matches for type names.
Validate all inputs
- Type name validation: Ensure type names match expected formats.
- Assembly restrictions: Limit types to known, trusted assemblies.
- Version checking: Consider version-specific type restrictions.
Handle unknown types securely
- Throw exceptions: Always throw for unauthorized types.
- Log attempts: Consider logging unauthorized access attempts.
- Clear error messages: Provide specific rejection reasons for debugging.
Regular maintenance
- Audit regularly: Review and update the allowed type list.
- Remove unused types: Eliminate permissions for types no longer needed.
- Document decisions: Maintain clear documentation of why each type is permitted.
Test your configuration
After configuring BinaryFormatter support, test your application to ensure it works correctly:
- Verify clipboard operations: Test both storing and retrieving data with your custom types.
- Test type resolver: Confirm that unauthorized types are properly rejected.
- Monitor security: Watch for any unexpected type resolution attempts.
- Performance testing: Ensure the type resolver doesn't significantly impact performance.
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
Plan your migration strategy
While BinaryFormatter support provides temporary compatibility, develop a migration plan to move to the new type-safe APIs:
- Identify usage: Catalog all clipboard operations using custom types.
- Prioritize migration: Focus on the most security-sensitive operations first.
- Update incrementally: Migrate one operation at a time to reduce risk.
- Test thoroughly: Ensure new implementations provide equivalent functionality.
- Remove BinaryFormatter: Disable support once migration is complete.
Clean up resources
Once you've migrated to the new type-safe clipboard APIs, remove the BinaryFormatter configuration to improve security:
- Remove the
System.Runtime.Serialization.Formatterspackage reference. - Remove the
EnableUnsafeBinaryFormatterSerializationproperty from your project file. - Remove the
Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerializationsetting from yourruntimeconfig.json. - Delete type resolver implementations that are no longer needed.
Related content
.NET Desktop feedback