As we move closer to the release of .NET 9, a significant change is on the horizon that will impact many developers: the removal of BinaryFormatter from the .NET runtime. Since its introduction, BinaryFormatter has been a go-to for developers needing an easy way to serialize and deserialize objects. However, its convenience has come at a significant cost—security.
The Security Risks of BinaryFormatter
The core issue with BinaryFormatter, and similar serialization mechanisms, is the ability to allow untrusted data to dictate the objects being created. This flexibility makes it a prime target for security exploits, particularly Remote Code Execution (RCE) attacks. With BinaryFormatter, if an attacker can control the serialized data, they can potentially manipulate your application into executing arbitrary code. This risk is not theoretical—real-world vulnerabilities have been found in major applications like Microsoft Exchange, Azure DevOps, and SharePoint, all due to misuse of BinaryFormatter.
The Path to Deprecation
Recognizing these risks, Microsoft began phasing out BinaryFormatter starting with .NET Core 1.0, initially removing it entirely. However, due to customer demand, it was reintroduced in .NET Core 1.1. Since then, the .NET team has worked to gradually reduce its usage, making it less accessible in newer project types and providing warnings to developers. With .NET 9, this journey reaches its conclusion as BinaryFormatter is fully removed from the runtime. Attempting to use it will now result in exceptions being thrown.
Why BinaryFormatter Can’t Be Fixed
Some might wonder why Microsoft doesn’t simply “fix” BinaryFormatter. The reality is that the design flaws are too fundamental. Any attempt to secure it would require such extensive changes that it would essentially be an entirely new API. Moreover, existing workarounds like serialization binders have proven to be inadequate, as there are code paths in BinaryFormatter that bypass these controls altogether.
What Developers Need to Do
For developers currently using BinaryFormatter, it’s crucial to start planning your migration strategy now. There is no one-size-fits-all replacement, as the right choice depends on your specific needs. Some teams at Microsoft have successfully migrated to alternatives like ProtoBuf, while others have adopted JSON-based serializers for their text-based data. The key is to find a serializer that suits your use case, whether it’s for network transport, storage, or other purposes.
If you’re working with legacy data or systems where you can’t immediately migrate away from BinaryFormatter, Microsoft is providing a “safe reader” tool. This tool allows you to read and inspect BinaryFormatter payloads without deserializing them into objects, giving you a safer way to handle legacy data during the transition period.
The Unsupported Compatibility Package
For those who simply cannot migrate away from BinaryFormatter right now, Microsoft offers an unsupported NuGet package that reintroduces the functionality. However, it’s important to emphasize that this package will not receive any security updates. Using it is essentially accepting the risk of potential vulnerabilities, which is why it should only be considered as a last resort.
Preparing for .NET 9 and Beyond
As .NET 9 approaches, it’s essential to review your applications and identify any usage of BinaryFormatter. Begin transitioning to safer serialization methods and consider how you’ll handle existing serialized data. Whether you choose JSON, ProtoBuf, or another format, the important thing is to make the change before .NET 9 forces your hand.
By moving away from BinaryFormatter, you’ll not only improve the security of your applications but also position them to better take advantage of future .NET advancements. The effort to migrate might seem scary, but it’s a necessary step to ensure the long-term safety and maintainability of your software. Let’s see a code example as well to showcase how you can migrate your existing code.
Before: Using BinaryFormatter
In this example, we have a simple class Person, and we’re serializing and deserializing it using BinaryFormatter.
[Serializable]
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
}
var customer = new Customer { Id = 1, Name = "John Doe", Email = "john.doe@example.com" };
using (FileStream stream = new FileStream("customer.bin", FileMode.Create))
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Serialize(stream, customer);
}
using (FileStream stream = new FileStream("customer.bin", FileMode.Open))
{
BinaryFormatter formatter = new BinaryFormatter();
Customer deserializedCustomer = (Customer)formatter.Deserialize(stream);
Console.WriteLine($"Deserialized Customer: {deserializedCustomer.Name}, {deserializedCustomer.Email}");
}
After: Migrating to protobuf-net
To migrate to protobuf-net, you’ll need to do the following:
- Install the
protobuf-netNuGet package. - Decorate your classes with
protobuf-netattributes to define the serialization schema. - Replace the
BinaryFormatterserialization and deserialization code withprotobuf-netequivalent.
using ProtoBuf;
var customer = new Customer { Id = 1, Name = "John Doe", Email = "john.doe@example.com" };
// Serialize object to a binary file using protobuf-net
using (FileStream stream = new FileStream("customer.bin", FileMode.Create))
{
Serializer.Serialize(stream, customer);
}
// Deserialize object from a binary file using protobuf-net
using (FileStream stream = new FileStream("customer.bin", FileMode.Open))
{
Customer deserializedCustomer = Serializer.Deserialize<Customer>(stream);
Console.WriteLine($"Deserialized Customer: {deserializedCustomer.Name}, {deserializedCustomer.Email}");
}
[ProtoContract]
public class Customer
{
[ProtoMember(1)]
public int Id { get; set; }
[ProtoMember(2)]
public string Name { get; set; }
[ProtoMember(3)]
public string Email { get; set; }
}
Conclusion
Migrating from BinaryFormatter to protobuf-net (or another modern binary serializer) requires some adjustments in your code, particularly around how your classes are decorated and how serialization is handled. However, the benefits of improved security and performance make it a worthwhile change.
The removal of BinaryFormatter in .NET 9 is a critical step forward in securing .NET applications. While the transition may require significant effort, the benefits in terms of security and future-proofing are well worth it. If you haven’t started planning your migration yet, now is the time to do so.
Affiliate promo
If you love learning new stuff and want to support me, consider buying a course from Dometrain using this link: Browse courses – Dometrain. Thank you!
Leave a comment