-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Announcement: BinaryFormatter is being removed in .NET 9 #98245
Comments
Tagging subscribers to this area: @dotnet/area-meta Issue DetailsEver since .NET Core 1.0, we in .NET Security have been trying to lay In .NET Core 1.0 we removed It's not that it’s binaryWe pick on There are classes within .NET which, by design, can run arbitrary code, and if you try to deserialize those through any deserialization code that creates objects based on the payload you end up with “Remote Code Execution” (RCE), a class of security vulnerability that allows the attacker to run their own code within your application, under the privileges your application has. This has been a known problem as far back as 2012, when the Black Hat security conference featured the canonical presentation on .NET’s version of the problem, Are you my type? Breaking .NET through Serialization, James Forshaw. There’s even a GitHub repository dedicated to hunting and showing these types of attacks in .NET, https://github.com/pwntester/ysoserial.net. Imagine your code has a class DeleteAllMyData. If you accept a payload from untrusted input, for example a web page, an attacker can create a payload that contains an instance of DeleteAllMyData and when you deserialize that payload you will get a chance to discover if your database backups actually restore. Other .NET serialization formats (and serialization formats in other languages and frameworks) allow for class information to be passed in a payload, to pick on our own framework Even JSON.NET has an opt-in option to allow the creation of a payload that specifies class names, and that is a text format, not a binary one. Other JSON deserializers (both .NET and Java) suffered from the same problem, as demonstrated at Black Hat 2017, Friday the 13th JSON Attacks, Alvaro Muñoz & Oleksandr Mirosh. Your takeaway should be that there’s nothing inherently wrong with a binary payload, it’s the fact that the Real Word Examples of BinaryFormatter security bugsWithin Microsoft,
There has been an outright ban on using BinaryFormatter is not fixable
Serialization binders, a type of class used to inspect types during deserialization, are often proposed as a control mechanism to stop The plan for .NET 9For the last few .NET releases newly introduced project types such as Blazor or MAUI projects have been unable to use Our current intentions are:
The safe reader will allow you to evaluate the classes within a public class Node
{
public SomeTypeDescriptor ObjectType;
public Dictionary<string, object> Fields;
} We will use a new class for the object type descriptor (whose name is yet to be decided) to impede naïve developers calling Type.GetType(theNodeType) and reintroducing the same fundamentally insecure behavior that we are trying to stop. Say you had had a class This graph will allow you to both validate the types in the payload against types you are expecting and provide data you can use to rehydrate your objects. Preparing for migrationTo migrate away from The first step to migrating away from At Microsoft, a lot of teams have moved from There are a lot of choices out there, but we cannot pick a winner for you. Each potential serializer has pluses and minuses, and how you weigh these is a decision only you can make for your application. Factors can include speed, memory consumption, storage costs, whether it is human readable, whether it is standards based and so on. The ideal safe use of the proposed SafeReader class is to perform conversions of existing data into your chosen new serialization format and run this conversion in as much isolation as possible, for example a virtual machine or a container, again analyzing the types in each payload, rehydrating objects, and then reserializing them in your chosen serialization format. If your class properties are primitive types, for example int, bool, or strings then the proposed safe reader could work as part of a replacement deserialization mechanism at runtime, leaving you to do the object rehydration once you have validated the types are those you expect. Or, if you accept the risk of Remote Code Execution, or, if you are sure that no-one can tamper with your messages or data (such a promise is extremely hard to make for online systems or for desktop or mobile apps) then you might consider using the proposed NuGet package. If you choose to take this route, please remember that this package is unsupported. We will not fix security issues in SummaryIf you are using You should evaluate how much backwards compatibility you need, and how long you need it for. Finally, you need to get off
|
Added
Tagging @dotnet/compat for awareness of the breaking change. |
I saw this in my RSS feed for the .NET blog, but it seems like the post itself has been taken down? I'm curious what's going on there. In any case, I do remember being warned against using |
Have you considered adding a migration path for I'm thinking about this class in particular since I'm using I suppose the new NuGet package could be a problem for framework classes in the long run, since comments such as |
@Nacimota The blog thing was totally my fault. I missed the new internal guidance to put things like this on github first :) |
@ltrzesniewski As I said above, we can't choose an alternative for you, even for things marked [Serializable]. For cookies, the use of any serializer that allows type specification in the payload is a bad idea, unless you're also signing that payload with an HMAC, and then managing the keys. If you're not, your inbound cookies are a vector for remote code execution. It's interesting you mention updates, I don't think we never promised compatibility in serialized payloads between major version updates, we just tried hard to make sure it didn't break. |
IIRC the last time I checked, the But that was a while ago, and I don't remember which part of the data I was unable to retrieve back then, so I guess I'll give it another try, and I'll open a separate issue if there's still anything lacking. In any case, I was mentioning this example as there may be other classes similar to |
If I'm reading the design doc from 2020 correct, (I am aware that if they are removed I can switch to using ExceptionDispatchInfo.SetRemoteStackTrace for most of my use case of transmitting exceptions over the network.) |
Was rather happy to see the title and that it would finally be removed so companies would stop postponing taking action. Companies will not think for a second to just go with the new package and put up a rule for scanners that will accept any issue raised by the package, under the thought "we will migrate in future" but which gets delayed for potentially forever till .NET Framework is officially unsupported. (I know a few who will be like that) Not to mention companies who are not yet over to .NET and their manager will use this as an additional excuse to moan about to not to migrate from .NET Framework to .NET because it blocks them. Really hope it gets fully removed, or at least in next LTS version. As .NET really needs scrubbing obsolete code instead of letting it roam around. |
For older version of dotnet is there a way to disable it? |
Since .NET 5, it is possible to use |
@Duranom unfortunately we're stuck in a tricky situation with this. When we released .NET Core 1.0, with BinaryFormatter removed entirely someone took the code from .NET Framework, built it into a nuget package and released it. Additionally, there were a lot of very emails from large customers to management stating people couldn't migrate from framework because it was missing. We did debate the "burn it all down" approach, but pragmatism steered us in this direction. With the new package we have something under our own control, which we will flag as code containing a security vulnerability. This will light up errors during builds you'll have to suppress. It will also make it easy for security scanners to detect the continuing use of BinaryFormatter, and, if you're not using a vulnerability scanner (because they can be expensive) a simply search over your hard drive will allow you to look for the new package. The package will also allow folks to move forward to .NET 9, or 10, or 11 etc., allowing them to keep going when a .NET version falls into end of life and no longer gets security patches. |
What'll be the story with .NET Standard? Should .NET 9 not support netstandard2.0 and netstandard2.1 since they specify BinaryFormatter support? Seems like removal of an API is breaking change that would justify going to netstandard3.0 (assuming netstandard follows semantic versioning rules). I have a library that I ship as a netstandard2 NuGet package, so this puts me in a weird spot because now I have to pull the BinaryFormatter out of the package, which will break some customers' existing .NET 6/7 apps. While it's reasonable for me to tell a customer that it's time to move to protobuf if they want .NET 9, it's not going to be pleasant to force existing .NET 6 customers to migrate to protobuf because of a change in another runtime that they aren't targeting. So it would be nice if I could add a new, BinaryFormatterless netstandard3 target to my NuGet package that only .NET 9+ apps would be able to pull in. |
@markwaterman These days you target the TFM unless you're sharing. I don't think you're going to see a .NET Standard 3.0, but those folks could surprise me. However when we say we're removing it we are removing the functionality, not the API. The BinaryFormatter class will still be there, it will just throw when you try to use it. The .NET Standards are for API surface, not functionality, so technically we're fine. If you're using BinaryFormatter in your nuget package you're already in an odd place, because it can already be disabled and uncallable in some project types. As I'm the security tpm my advice would be to stop using it on all versions, including netstandard2.0 and 2.1 because it's too dangerous to keep using. But, if you don't want to force your customers you'd pull in the new unsupported package, and keep going, with all the security warnings that will entail. You face the same problem we have, and we've chosen to force the issue. |
"Unsupported.BinaryFormatter) which you could add to your projects. With a small config change in your app, BinaryFormatter will appear again" |
@osexpert Config will tell the runtime to swap the inbox "throw on every call" version of BinaryFormatter with the unsupported, insecure package which provides the old serialization and deserialization behaviour. The format of the config entry is to be decided. |
So the runtime BinaryFormatter will somehow forward to a BinaryFormatter in Microsoft.Unsupported.BinaryFormatter? So the runtime will need to know about\rely on\depens on Microsoft.Unsupported.BinaryFormatter somehow? Could the BinaryFormatter in Microsoft.Unsupported.BinaryFormatter be in its own namespace, without any forwarding from the runtime BinaryFormatter? It would not be drop-in compatible (would need to change the namespace and recompile), but it feels cleaner and would allow the runtime to not know\care about Microsoft.Unsupported.BinaryFormatter and vice versa? |
The runtime will know about the config hooks but you don't have to set them. The unsupported version will probably (we're discussing it and there's a huge white paper being written) be in its own namespace where you don't have to hook it up, so the in box version will still throw. The config is there as a dangerous re-enablement feature. |
My expectation in that case is that you could add validation to ensure that you are not deserializing a malicious version of an object that you expect. Perhaps you would use text serialization, which may allow analyzing the object better, or perhaps you would deserialize it essentially in a sandbox to allow inspection of the object. That's what we expect the safe parser to offer, an object graph you can walk to make sure the classes are what you expect, but a constrained serializer where you have to specify the object you want to deserialize into is going to be simpler and safer to use. |
More security more better. Well come. |
Great write-up. Does anyone know if this has been created? |
Since WebForms is still officially supported, I take it this means the And another question. You said it would be impossible to change However, wouldn't something like a required list of known types that the developer needs to provide substantially improve security of this class? Wouldn't that be somewhat equivalent to what happens with XML and Json inheritance serialization today, where you provide an explicit list of known types in the base class so that the serializer can create those different objects based on some sort of discriminator in the payload? If |
@julealgon WinForms is on a path to move away from BinaryFormatter use in .NET. .NET framework will remain as is, it is not affected by any of this. Requiring a list of known types, well, you're making my point for me I'm afraid. Once the deserialization API changes it's just not BinaryFormatter. The whole point of BinaryFormatter is unconstrained serialization. |
@beyond-code-github Not yet. It underwent API review this week. |
I take it you meant to say Windows Forms there? I'm talking about the ASP.NET WebForms framework which is only available in .NET Framework.
My point was that this wouldn't be that massive of a change in terms of API contract, and could fit the bill for many simpler scenarios with a very small code change. Wouldn't that be worth pursuing? |
Oof you're right, editing. Not enough coffee yet :) But any API change like that is a massive change. Anyone using BinaryFormatter would be immediately broken. It's exactly what we want to avoid. |
I'm sorry @blowdart , I see your update but I already knew the situation regarding WinForms. Can you clarify on the rest of my post in terms of WebForms though? |
WebForms is a .NET Framework thing. Nothing here affects Framework in anyway. |
What is the current state of removing binary formatter? I'm testing .Net 9 Preview 4 with my .Net 6 controls which have serializable objects and use them for drag&drop. Apparently, DataObject class still uses binary serialization and it only fails on attempt to deserialize with exception about not supported binary formatter. What are your plans about this, should I try to fix it from my side and serialize object manually before creating DataObject or you will try to use other formatter from your side? Is it time to post you bugs about this? It affects both WPF and WinForms, only exception text I get is a bit different. |
@IrinaPykhova - @JeremyKuhne, @adamsitnik, @lonitra are all working hard on getting all the pieces lined up for the final removal of BinaryFormatter, and @KlausLoeffelmann is working on Analyzers to help people understand when they unexpectedly (in a WinForms app) are doing something that may eventually use BF. We should be ready for a broad update very soon, the plan approved, and we are all executing. Feel free to ping us in the designer repo for details, specifically on how it may impact control developers in the short term. I'll circle back here as soon as we have firm timelines so we can share the plan and timelines broadly. |
@blowdart Glad to see you're finally getting your wish! I remember chatting with you about this years ago. Honestly, the number of trivial RCE bugs I found in .NET apps plummeted after BinaryFormatter was deprecated. Removing the classes entirely is one more step toward eliminating these intrinsic vulnerabilities. |
Ever since .NET Core 1.0, we in .NET Security have been trying to lay
BinaryFormatter
to rest. It’s long been known by security practitioners that any deserializer, binary or text, which allows its input to carry information about the objects to be created is a security problem waiting to happen. There is even a Common Weakness Enumeration (CWE) that describes the issue, CWE-502 “Deserialization of Untrusted Data” and examples of this type of vulnerability run from security issues in Exchange through security issues in Apache. Within Microsoft the use ofBinaryFormatter
with untrusted input has caused many instances of heartache, late nights, and weekend work trying to produce a solution.In .NET Core 1.0 we removed
BinaryFormatter
entirely due to its known risks, but without a clear path to using something safer customer demand brought it back to .NET Core 1.1. Since then, we have been on the path to removal, slowly turning it off by default in multiple project types but letting you opt-in via flags if you still needed it for backward compatibility. .NET 9 sees the culmination of this effort with the removal ofBinaryFormatter
. In .NET 9 these flags will no longer exist and the in-box implementation ofBinaryFormatter
will throw exceptions in any project type when you try to use it. However, if you are committed to using a class that cannot be made secure you will still be able to.It's not that it’s binary
We pick on
BinaryFormatter
because of its ubiquitous use, but the problem is not that the payload is binary, the problem is that the payload tells .NET what objects to create. That capability is what makesBinaryFormatter
so popular, you can throw pretty much any class at it, and it will serialize and deserialize it correctly without developers having to do any work. However, it is also what makes it so dangerous.There are classes within .NET which, by design, can run arbitrary code, and if you try to deserialize those through any deserialization code that creates objects based on the payload you end up with “Remote Code Execution” (RCE), a class of security vulnerability that allows the attacker to run their own code within your application, under the privileges your application has.
This has been a known problem as far back as 2012, when the Black Hat security conference featured the canonical presentation on .NET’s version of the problem, Are you my type? Breaking .NET through Serialization, James Forshaw. There’s even a GitHub repository dedicated to hunting and showing these types of attacks in .NET, https://github.com/pwntester/ysoserial.net.
Imagine your code has a class DeleteAllMyData. If you accept a payload from untrusted input, for example a web page, an attacker can create a payload that contains an instance of DeleteAllMyData and when you deserialize that payload you will get a chance to discover if your database backups actually restore.
Other .NET serialization formats (and serialization formats in other languages and frameworks) allow for class information to be passed in a payload, to pick on our own framework
SoapFormatter
,LosFormatter
,NetDataContractSerializer
andObjectStateFormatter
all exhibit the same dangerous characteristics.Even JSON.NET has an opt-in option to allow the creation of a payload that specifies class names, and that is a text format, not a binary one. Other JSON deserializers (both .NET and Java) suffered from the same problem, as demonstrated at Black Hat 2017, Friday the 13th JSON Attacks, Alvaro Muñoz & Oleksandr Mirosh.
Your takeaway should be that there’s nothing inherently wrong with a binary payload, it’s the fact that the
BinaryFormatter
payload contains instructions on the classes to be created, and what should go into that class’s properties that make it so dangerous. Other serializers, including some JSON serializers, also have the same ability, making them equally dangerous to use.Real Word Examples of BinaryFormatter security bugs
Within Microsoft,
BinaryFormatter
has been the cause of a quite a few security problems, including:BinaryFormatter
and granted RCE.BinaryFormatter
and once again lead to RCE.There has been an outright ban on using
BinaryFormatter
for new projects at Microsoft for years, and an ongoing effort to remove it from older code for almost as long as the usage ban in new code. We have added code analyzers, warnings, and public documentation, and yet still we see people using it and ending up with RCE bugs. It feels like we need to do more.BinaryFormatter is not fixable
BinaryFormatter
has other problems around algorithmic complexity, unbounded cache growth, and unintended object graph edge manipulation. If we take out one insecure piece, replace it with a secure piece, and keep going until all the insecure pieces are replaced there is not much of the originalBinaryFormatter
left. A secure version would have a vastly different API shape and it could not be a drop-in replacement. It would require massive refactoring of application code to use, so, really, would it even beBinaryFormatter
anymore?Serialization binders, a type of class used to inspect types during deserialization, are often proposed as a control mechanism to stop
BinaryFormatter
doing bad things, but there are code paths inBinaryFormatter
that bypass binders by design, and writing a correct binder that isn’t vulnerable to a Denial of Service (DoS) attack is extremely hard.The plan for .NET 9
For the last few .NET releases newly introduced project types such as Blazor or MAUI projects have been unable to use
BinaryFormatter
at all. ASP.NET projects are particularly vulnerable as their entire purpose is to take untrusted input and were also part of the ban. For some, older, project types you have been able to opt into usingBinaryFormatter
by setting a flag.Our current intentions are:
BinaryFormatter
will throw exceptions when you try to use it, even if you set any of the settings that previously enabled its use. We are also removing these settings.BinaryFormatter
will appear again, which is useful if you judge the risk ofBinaryFormatter
acceptable for your use cases. This NuGet package will be permanently marked as vulnerable so that dependency and code scanners can detectBinaryFormatter
usage easily.BinaryFormatter
payload and it will return an object graph describing the payload. Each node in the graph will represent an object in the payload and each edge represents a reference between objects.The safe reader will allow you to evaluate the classes within a
BinaryFormatter
payload, providing a representation of the object graph with each node containing information on the object type, and a list of its fields and their values. The node structure we are considering looks as follows:We will use a new class for the object type descriptor (whose name is yet to be decided) to impede naïve developers calling Type.GetType(theNodeType) and reintroducing the same fundamentally insecure behavior that we are trying to stop.
Say you had had a class
Customer
and a classAddress
, and your customer had aShippingAddress
andBillingAddress
, both of which are properties in the customer class, with a type ofAddress
. The fields within theCustomer
object node would be something like{ "ShippingAddress", node_for_ship_addr }
and{ "BillingAddress", node_for_bill_addr }
.This graph will allow you to both validate the types in the payload against types you are expecting and provide data you can use to rehydrate your objects.
Preparing for migration
To migrate away from
BinaryFormatter
is going to take work. There is no drop-in replacement, nor is there a single path that fits everyone because everyone’s classes are different and there are too many varied use cases.The first step to migrating away from
BinaryFormatter
should be selecting a new serialization format. Remember that binary representation is safe if the produced payload does not instruct the deserialization process to create types. The same restriction applies to text-based formats.At Microsoft, a lot of teams have moved from
BinaryFormatter
to ProtoBuf (you will even see some familiar names in the commit history for the C# version). Other teams, whose data is text and where textual representation have advantages like easy searching have moved to JSON.There are a lot of choices out there, but we cannot pick a winner for you. Each potential serializer has pluses and minuses, and how you weigh these is a decision only you can make for your application. Factors can include speed, memory consumption, storage costs, whether it is human readable, whether it is standards based and so on.
The ideal safe use of the proposed SafeReader class is to perform conversions of existing data into your chosen new serialization format and run this conversion in as much isolation as possible, for example a virtual machine or a container, again analyzing the types in each payload, rehydrating objects, and then reserializing them in your chosen serialization format.
If your class properties are primitive types, for example int, bool, or strings then the proposed safe reader could work as part of a replacement deserialization mechanism at runtime, leaving you to do the object rehydration once you have validated the types are those you expect.
Or, if you accept the risk of Remote Code Execution, or, if you are sure that no-one can tamper with your messages or data (such a promise is extremely hard to make for online systems or for desktop or mobile apps) then you might consider using the proposed NuGet package. If you choose to take this route, please remember that this package is unsupported. We will not fix security issues in
BinaryFormatter
.Summary
If you are using
BinaryFormatter
now you should start looking at alternative serialization formats and decide what works for you in your specific circumstance, be it saved object graphs, network transports, temporary round tripped data, or any other type of data.You should look at all your saved data and plan to convert any data serialized by
BinaryFormatter
into the new serialization format you have chosen.You should evaluate how much backwards compatibility you need, and how long you need it for.
Finally, you need to get off
BinaryFormatter
, it is too dangerous to use any more and that is why it is leaving the .NET runtime.The text was updated successfully, but these errors were encountered: