Owner Levi Broderick
This document applies to the following framework versions:
- .NET 5.0+
Applications which target .NET Framework 2.x, .NET Framework 4.x, or .NET Core 3.1 are not affected by this proposal. BinaryFormatter
will continue to work as expected in those applications. Consumers of BinaryFormatter
are still advised to consult the BinaryFormatter
security guide to help inform their risk assessment.
As part of modernizing the .NET development stack and improving the overall health of the .NET ecosystem, it is time to sunset the BinaryFormatter
type. BinaryFormatter
is the mechanism by which many .NET applications find themselves exposed to critical security vulnerabilities, and its continued usage results in numerous such incidents every year across both first-party and third-party code.
We have messaged previously that BinaryFormatter
is not safe for untrusted input, but outside of the security community this guidance hasn't really taken root. This is partly because there's already a critical mass of code using BinaryFormatter
, and developers are generally disincentivized from changing existing code. It's also partly due to the fact that threat modeling is an advanced topic not well understood by the majority of developers. Without the .NET team introducing a forcing function, the ecosystem holistically does not feel the need to improve.
See the BinaryFormatter
security guide for more information on the problem space.
BinaryFormatter
also leads to the creation of fragile and non-versionable types. Its operation relies primarily on private reflection over an object's instance fields. This ties the serialized payload format to internal implementation details of the target types. Updating or improving the implementation details of the target type often creates compatibility problems between serialized versions.
Encouraging migration away from BinaryFormatter
has the additional benefit of making applications more friendly to linker trimming. .NET is heavily invested in linker trimming as a means of reducing the deployed footprint of .NET-based applications and web services. This capability relies upon the compiler being able to statically analyze what code paths are reachable from the application. BinaryFormatter
and other serializers which perform unbounded reflection based on input from the wire are not conducive to this goal, as the real type information isn't known until the Type.GetType
call at runtime. We would like to move these applications onto other serializers (such as those in the System.Text.Json package) to help drive our ability to enable trimming throughout the ecosystem.
- Allow disabling
BinaryFormatter
via an opt-in feature switch- ASP.NET projects disable
BinaryFormatter
by default but can re-enable - WASM projects disable
BinaryFormatter
with no ability to re-enable - All other project types (console, WinForms, etc.) enable
BinaryFormatter
by default
- ASP.NET projects disable
- .NET produces guidance document on migrating away from
BinaryFormatter
- All outstanding
BinaryFormatter
-related issues resolved won't fix - Introduce a
BinaryFormatter
tracing event source Serialize
andDeserialize
marked obsolete as warning
- No new
[Serializable]
types introduced - No new calls to
BinaryFormatter
from any first-party dotnet org code base - All first-party dotnet org code bases begin migration away from
BinaryFormatter
- All first-party dotnet org code bases continue migration away from
BinaryFormatter
- References to
BinaryFormatter
APIs marked obsolete as warnings in .NET 5 now result in build errors- A back-compat switch is made available to turn these back to warnings
- All first-party dotnet org code bases complete migration away from
BinaryFormatter
BinaryFormatter
disabled by default across all project types- All not-yet-obsolete
BinaryFormatter
APIs marked obsolete as warning - Additional legacy serialization infrastructure marked obsolete as warning
- No new
[Serializable]
types introduced (all target frameworks)
- Remainder of legacy serialization infrastructure marked obsolete as warning
BinaryFormatter
infrastructure removed from .NET- Back-compat switches also removed
In .NET 5 we will introduce a feature switch to selectively allow BinaryFormatter
to be disabled in an application. This can be accomplished in one of three ways, as demonstrated below.
Disabling BinaryFormatter
via the application's .csproj:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<EnableUnsafeBinaryFormatterSerialization>false</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
</Project>
Disabling BinaryFormatter
via the application's runtimeConfig.template.json:
{
"configProperties": {
"System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization": false
}
}
Disabling BinaryFormatter
via an explicit call to AppContext.SetSwitch
:
AppContext.SetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", false);
If this switch is set to true, BinaryFormatter
serialization + deserialization will be enabled for the application. If this switch is set to false, BinaryFormatter
serialization + deserialization will be disabled for the application and will throw NotSupportedException
. Setting the value to false also allows the .NET linker to trim away BinaryFormatter
-related code paths, reducing the total disk footprint of the resulting binary.
If the switch is not set, the target project type will determine whether BinaryFormatter
serialization + deserialization is allowed or disallowed. Note that this is determined by the application type. Libraries cannot set this switch via their own .csproj or runtimeConfig.json files.
We expect high-sensitivity applications to set this switch to false as part of an overall attack surface reduction strategy. Any document we produce that details how to configure high-sensitivity applications should recommend setting this switch. The scenario for setting this switch to false eagerly is that the developer does not expect the application to invoke
BinaryFormatter
-related code paths during normal operations, and they want some extra assurance that a latent bug or untested edge case in a dependency cannot re-activate theBinaryFormatter
infrastructure.
Certain project types - such as WinForms projects - may use
BinaryFormatter
as an implementation detail. DisablingBinaryFormatter
for these project types in .NET 5 may interfere with the correct functionality of the application. See later in this document for the approach the runtime will take for ensuring this switch works correctly in future .NET versions.
Disabling BinaryFormatter.Serialize
and Deserialize
via the feature switch doesn't affect the behavior of any other APIs on BinaryFormatter
. For instance, the existing BinaryFormatter
property getters and setters will continue to work. Types like ObjectManager
which are intended to support the BinaryFormatter
infrastructure will also work if the application has direct calls into those APIs. If the application does not have direct calls to these APIs, the linker will likely prune these now-unused types when it prunes the BinaryFormatter
implementation.
There is no API to query whether a call to BinaryFormatter.Serialize
or BinaryFormatter.Deserialize
will fail due to this switch being set. Callers can instead catch the NotSupportedException
and query the AppContext
manually to see if support for serialization has been disabled. An example of this is provided below.
BinaryFormatter formatter = new BinaryFormatter();
try
{
formatter.Serialize(/* ... */);
}
catch (NotSupportedException ex)
{
if (AppContext.TryGetSwitch("System.Runtime.Serialization.EnableUnsafeBinaryFormatterSerialization", out bool isEnabled)
&& !isEnabled)
{
// BinaryFormatter serialization support is disabled; handle it here
}
else
{
throw; // BinaryFormatter serialization is enabled; rethrow exception
}
}
The libraries team will also document known scenarios that will be broken when the BinaryFormatter
code paths are disabled. One such example is attempting to read .resx files with embedded BinaryFormatter
-serialized objects. These scenarios are not expected to be common in web applications, but they do nevertheless exist to a limited extent.
For WASM (Blazor Client) projects, the BinaryFormatter feature switch will be hardcoded to disabled. The implementations of BinaryFormatter.Serialize
and BinaryFormatter.Deserialize
will throw NotSupportedException
. There is no ability for WASM applications to re-enable this feature through configuration or through a runtime switch. This hardcoded behavior extends to all other novel application types that might be introduced after the .NET 5 timeframe, including mobile apps and other restricted environment configurations.
Starting with ASP.NET 5.0, the implementations of BinaryFormatter.Serialize
and BinaryFormatter.Deserialize
will by default throw NotSupportedException
. This is being done to reduce the attack surface area of web applications. This project type has historically been the largest target of deserialization exploits.
To re-enable the BinaryFormatter
code paths in ASP.NET projects, the application must set the feature switch described above in their .csproj, and as demonstrated below.
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>
<!-- WARNING: re-enabling the switch below could subject your application to critical security vulnerabilities -->
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
</Project>
For other project types - such as console, WinForms, and WPF - BinaryFormatter
will continue to be enabled by default. No configuration is necessary if the app wishes to continue using the BinaryFormatter
APIs. However, developers can still disable BinaryFormatter
by using the feature switch mechanism mentioned previously.
The .NET team will produce a document showing how to accomplish various serialization scenarios in manners that don't involve BinaryFormatter
. Some examples of scenarios that we can show:
- Reading the raw binary contents of a graphic and constructing an Image from it.
- Using System.Text.Json to serialize / deserialize object models, including dictionaries and other collection types.
- Demonstrating how to serialize / deserialize
CultureInfo
via just the culture name. - Transmitting
Exception
objects safely across the wire.
We need to be prepared for the fact that not every scenario will be achievable. For example, .NET Framework allows serializing delegates, but .NET Core doesn't. This is by design and no replacement is intended. As another example, System.Text.Json doesn't allow deserializing cyclic object graphs by default. We would demonstrate how to opt in to these features while calling attention to the fact that consumers should consider the security impact of doing so.
We are also intentionally not providing sample code on the use of a SerializationBinder
as a security mechanism, as we cannot guarantee the safety of this design and should not promote its usage as such.
We'll perform a final pass through all the BinaryFormatter
-related issues opened on GitHub, resolving all of them won't fix and pointing them to this document. There are only three exclusions to this pass:
- Functional regressions introduced as part of an in-place servicing release.
- Severe performance regressions introduced as part of an in-place servicing release.
- Open issues which track implementing this deprecation plan.
All other issues, including incompatibility between .NET Framework + .NET Core, or asking for new types to be marked [Serializable]
, or asking for new features or bug fixes, will be closed.
To help app developers detect unexpected calls to BinaryFormatter
within dependencies, we will enable a tracing event source within BinaryFormatter
itself. This tracing source will provide notification that BinaryFormatter.Serialize
or BinaryFormatter.Deserialize
was called, and developers can use this information as part of their own process of auditing BinaryFormatter
usage.
It is anticipated that this mechanism will use the System.Diagnostics.DiagnosticSource infrastructure. More information on using this infrastructure is available at the user guide.
App developers can test-run their application with this trace listener enabled, and if they see no unexpected calls to BinaryFormatter
they can confidently set the AppContext setting to disable BinaryFormatter
in production. This allows them to limit their exposure to BinaryFormatter
-related vulnerabilities within their own services.
The Serialize
and Deserialize
methods on BinaryFormatter
, Formatter
, and IFormatter
will be marked obsolete as warning, as shown below.
namespace System.Runtime.Serialization.Formatters.Binary
{
public sealed class BinaryFormatter : IFormatter
{
[Obsolete("...", error: false, DiagnosticId = "...")]
public object Deserialize(Stream serializationStream);
[Obsolete("...", error: false, DiagnosticId = "...")]
public void Serialize(Stream serializationStream, object graph);
}
}
namespace System.Runtime.Serialization
{
public interface IFormatter
{
[Obsolete("...", error: false, DiagnosticId = "...")]
object Deserialize(Stream serializationStream);
[Obsolete("...", error: false, DiagnosticId = "...")]
void Serialize(Stream serializationStream, object graph);
}
public abstract class Formatter : IFormatter
{
[Obsolete("...", error: false, DiagnosticId = "...")]
public abstract object Deserialize(Stream serializationStream);
[Obsolete("...", error: false, DiagnosticId = "...")]
public abstract void Serialize(Stream serializationStream, object graph);
}
}
The error message text is TBD but will direct callers to the documentation available at https://aka.ms/binaryformatter. The diagnostic id will be set per the better obsoletion specification.
This is likely to be very noisy since it's the first change that will begin to produce warnings in our developer audience's own code. We will continue updating the guidance document beyond the .NET 5.0 timeframe so that it's clear what kinds of changes developers should make within their own code bases.
The behavior of the obsoleted APIs will not change. Applications which suppress or ignore the warning will see no runtime behavioral difference, assuming the "disable BinaryFormatter
entirely" feature switch mentioned previously in this document has not been utilized.
.NET 5.0 will be the final version of .NET that introduces [Serializable]
types. Beginning with .NET 6.0, no new types will be marked [Serializable]
, nor will the [Serializable]
attribute be added to existing types or to types ported from .NET Framework. Introducing serializable annotations runs counter to our goal of spinning down usage of BinaryFormatter
framework-wide.
This prohibition on new serializable types applies only to projects in the dotnet org on GitHub. Other frameworks, libraries, or applications built on .NET may continue to use [Serializable]
in their own code bases.
The exclusion is for dotnet org first-party projects which target netstandard2.0 or earlier. For these projects, Exception
-derived types should continue to be annotated as [Serializable]
so that they may be remoted across app domain boundaries. Any new Exception
-derived types that are defined in a library which is not available to netstandard2.0 callers will not be annotated with [Serializable]. We should be following our own guidance on how to safely serialize exception information without involving BinaryFormatter
.
Beginning with .NET 6.0, no new code introduced into the dotnet org GitHub repos may call into BinaryFormatter
, regardless of target runtime. Introducing such code would run counter to our goal of spinning down usage of BinaryFormatter
framework-wide. This prohibition does not apply to user or partner code. Those code bases may continue to use BinaryFormatter
as needed, though they are strongly discouraged from doing so.
This should not prevent us from refactoring existing first-party code which uses BinaryFormatter
. The intent of this policy is to prevent BinaryFormatter
's usage in new scenarios, not to prevent teams from maintaining their own code bases. Documentation, tests, and other "non-shipping" code is exempt.
The WinForms designer should enable new .resx formats which do not involve BinaryFormatter
, following our guidance on how to serialize such resources safely. These new .resx formats should be the default for any scenario which previously required BinaryFormatter
involvement.
MSBuild and other components which handle BinaryFormatter
-serialized payloads opaquely should continue to process these payloads. No action is required on their end unless they attempt to deserialize these payload.
If BinaryFormatter
is used in any other data persistence scenario (e.g., clipboard or app saved state), the area owners should consult the guidance document to create a safe alternative. All code paths which involve BinaryFormatter
must have equivalent non-BinaryFormatter
alternatives available, though these alternatives needn't have 100% feature parity. Area owners should make every effort to enable the non-BinaryFormatter
code paths as the default for creating new data. It remains allowable for these libraries to read existing data via BinaryFormatter
.
As part of this, dotnet org developers should audit the area of ownership for calls to Type.GetType
and related APIs, as these could function as unrestricted deserialization equivalents. Any call sites which deal with untrusted data should clearly be annotated as such in code comments. If necessary, the owners should open issues to track removing the dangerous code patterns or documenting the API as dangerous. This will allow us to make more informed estimates of our libraries' own exposure to potential RCE attacks.
In .NET 5, we marked several BinaryFormatter
-related APIs obsolete as warning (see the breaking change notice). In .NET 7, references to these APIs will produce build errors. This will cause compilation failures in applications which directly consume these APIs, and developers who call these APIs must take action in order to migrate their applications onto .NET 7.
An opt-in back-compat switch will be made available for applications which cannot migrate away from BinaryFormatter
in the .NET 7 timeframe. We will utilize the same compat switch that was introduced in .NET 5.
<Project>
<PropertyGroup>
<TargetFramework>net7.0</TargetFramework>
<!-- Setting the switch below turns the errors back to warnings. -->
<!-- WARNING: re-enabling the switch below could subject your application to critical security vulnerabilities. -->
<EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>
</Project>
No first-party dotnet org code base contains an enabled-by-default calls to BinaryFormatter
. The call sites may remain in the library but must be guarded by an opt-in switch. This opt-in switch must be specified by the app developer, not by an "I'm serialized using BinaryFormatter
" flag within the payload itself. All other payload formats must be written or read using a BinaryFormatter
alternative.
Importantly, none of our project types (WinForms / WPF / ASP.NET / mobile / etc.) can require developers to opt back in to allowing BinaryFormatter
for any greenfield development. Applications which require BinaryFormatter
for app-compat reasons must require developers to perform an explicit opt-in gesture to re-enable these code paths.
Beginning with .NET 8.0, the "disable BinaryFormatter
" feature switch is activated by default across all project types. Developers must perform an explicit opt-in gesture to re-enable these code paths. This will utilize the same compat switch introduced in .NET 5.
In .NET 8.0, the following APIs will be marked [Obsolete]
as error by default. These will utilize the existing SYSLIB0011
warning code. The errors can be converted back to warnings by using the <EnableUnsafeBinaryFormatterSerialization>
compat switch within the project file.
- The entirety of the
BinaryFormatter
type. - The entirety of the
IFormatter
interface. - The entirety of the
Formatter
type.
The following APIs will be marked [Obsolete]
as warning using a new warning code (tentatively SYSLIB0050
).
Newly obsolete types
IFormatterConverter
interface andFormatterConverter
concrete typeFormatterServices
static classIObjectReference
interfaceISurrogateSelector
andISerializationSurrogate
interfaces, and theSurrogateSelector
concrete typeISafeSerializationData
andSafeSerializationEventArgs
StreamingContextStates
enumObjectIDGenerator
ObjectManager
SerializationObjectManager
FormatterTypeStyle
,FormatterAssemblyStyle
, andTypeFilterLevel
enumsIFieldInfo
interface
(All APIs which expose these types through their public API surface would also be obsoleted.)
Newly obsolete members on existing types
Type.IsSerializable
public property (and all overridden implementations)TypeAttributes.Serializable
enum valueFieldInfo.IsNotSerialized
public propertyFieldAttributes.NotSerialized
enum valueISerializable.GetObjectData
method, but not the type itselfSerializationInfo
andStreamingContext
, all ctors, but not the types themselves
The following APIs will be marked [Obsolete]
as warning using a new warning code (tentatively SYSLIB0051
) and marked [EditorBrowsable(Never)]
.
- All externally visible (public or protected) serialization ctors:
.ctor(SerializationInfo, StreamingContext)
. - All public (not explicit or protected) implementations of
IObjectReference.GetRealObject
. - All public (not explicit or protected) implementations of
ISerializable.GetObjectData
.
The following APIs will not be obsoleted but will be marked [EditorBrowsable(Never)]
.
- The
SerializableAttribute
type - The
NonSerializedAttribute
type
These changes only affect applications targeting .NET 8+. If a project targets multiple runtimes, these new annotations will be #if
conditioned to apply only to .NET 8+.
The reason for using two new warning codes is that these warnings are directed at two different audiences. The first warning code (tentatively SYSLIB0050
) is directed toward people who invoke the legacy serialization infrastructure. This consists mostly of people who are writing serializers or of application developers who are doing something so specialized that they require direct calls back in to the serialization infrastructure.
The second warning code (tentatively SYSLIB0051
) is directed at library authors who extend existing [Serializable]
types. For example, this warning code would be observed by developers who both subclass types like Exception
or Dictionary<TKey, TValue>
and extend its serialization ctor. The warning code indicates that this is legacy serialization infrastructure intended only for compatibility, and it should not be adopted by new .NET applications.
Most developers who use
[Serializable]
or who implementISerializable
themselves will see no warnings, since those types are not being obsoleted at this time. For developers who see warnings because they call into base implementations' now-obsolete members, self-remediation guidance will be provided.
The following types are not being obsoleted at this time: SerializableAttribute
, NonSerializedAttribute
, OnSerializingAttribute
, OnSerializedAttribute
, OnDeserializingAttribute
, OnDeserializingAttribute
, ISerializable
. This would be too disruptive to the wider ecosystem at this time since other serializers within .NET understand these attributes (but do not honor them correctly). Rather, we must prioritize getting people off of legacy dangerous deserializers by providing better guidance and stepping stones before tackling this longer tail of work. To support that principle, these APIs will not be obsoleted until .NET 9. However, since SerializableAttribute
and NonSerializedAttribute
are within the System
namespace, it's prudent to mark them [EditorBrowsable(Never)]
as part of this wave.
In .NET 6, we had stopped attributing new types with [Serializable]
, carving out an exception only for Exception
-derived types in projects which targeted netfx or netstandard.
Starting with .NET 8, we will no longer attribute any new types with [Serializable]
, voiding the previously granted exception. (Already-shipped types will retain their existing annotations.)
When targeting .NET 9+, the following types will be marked [Obsolete]
as warning.
SerializableAttribute
NonSerializedAttribute
OnSerializingAttribute
OnSerializedAttribute
OnDeserializingAttribute
OnDeserializedAttribute
ISerializable
IDeserializationCallback
This should represent the balance of unobsoleted APIs which are strongly tied to the legacy serialization infrastructure.
In .NET 9.0, the entirety of the BinaryFormatter
infrastructure will be removed from the product. Here, "BinaryFormatter
infrastructure" refers to any API which was obsoleted (as error) as part of an earlier stage of this proposal, even if that API is not itself on the BinaryFormatter
type. The implementation of all such APIs will be to throw PlatformNotSupportedException
. The internal BinaryFormatter
implementation will no longer exist in .NET in any form and cannot be reenabled through any compat switch. Functionality may be moved to an unsupported out-of-band package.
All code bases which target net9.0 or above must be fully migrated away from BinaryFormatter
at this time. This applies to dotnet org first-party code, partner team code, and third-party user code.
There is currently no plan to remove the now-dead APIs themselves from the SDK. Doing so would prevent loading existing libraries which are compiled against those types. However, this may be suggested in the future either concurrent with or occurring after the final "obsolete as error" behavior.
What does this mean for already-deployed applications?
Nothing. There are no behavioral changes being proposed to BinaryFormatter
for existing in-support versions of .NET, including .NET Framework 2.x - 4.x or .NET Core 2.1 - 3.1. Applications already deployed on these frameworks will continue to work correctly and will enjoy full support for the lifetime of the product. We will not modify the behavior of BinaryFormatter
as part of any servicing release.
However, per the BinaryFormatter
security guide, we recommend that all applications migrate off of BinaryFormatter
as they find the opportunity to do so.
What serializer should be used instead of BinaryFormatter
?
This really depends on the scenario. XML and JSON serializers follow open standards and are often appropriate when the client and the server are written by two different entities. Compressed binary formats may be appropriate for proprietary protocols which want to minimize bandwidth usage. Some serializers opt for speed (total objects serialized or deserialized per second) at the expense of producing slightly larger payloads.
The BinaryFormatter
security guide contains recommendations for choosing a serializer appropriate for the given scenario.
Is BinaryFormatter
officially supported in .NET 5.0 and beyond?
Yes, per the limitations described in this document. As long as the type exists in the framework and has an implementation, support is available. For example, if an app which uses BinaryFormatter
behaves as expected in .NET 5.0 but fails after a .NET 5.0.x patch is applied, this regression should be reported to the product team for resolution.
See the earlier section section titled "All outstanding BinaryFormatter-related issues resolved won't fix" for more information on what categories of issues qualify for support.
Once
BinaryFormatter
is removed from the product (tentatively scheduled for .NET 9), it will no longer be supported by the product team in that release and in subsequent releases. For .NET 7, .NET Framework 4.x, and other runtimes which bundleBinaryFormatter
as part of the product, the functionality will continue to be supported throughout the normal support period of the .NET runtime itself, subject to the above conditions.
What about other BinaryFormatter
-equivalent types like LosFormatter
, ObjectStateFormatter
, SoapFormatter
, and NetDataContractSerializer
?
These types exist only in .NET Framework 2.x - 4.x. They do not exist in .NET Core or .NET 5.0+. These types will not be reintroduced in any future version of .NET. Applications which wish to migrate to .NET 5.0+ are required to move off of these types. Applications which continue targeting .NET Framework 2.x - 4.x are advised to move off of these types at their earliest opportunity. See the BinaryFormatter
security guide for more information.
Why not make BinaryFormatter
safe for untrusted payloads?
The BinaryFormatter
protocol works by specifying the values of an object's raw instance fields. In other words, the entire point of BinaryFormatter
is to bypass an object's typical constructor and to use private reflection to set the instance fields to the contents that came in over the wire. Bypassing the constructor in this fashion means that the object cannot perform any validation or otherwise guarantee that its internal invariants are satisfied. One consequence of this is that BinaryFormatter
is unsafe even for seemingly innocuous types such as Exception
or List<T>
or Dictionary<TKey, TValue>
, regardless of the actual types of T, TKey, or TValue. Restricting deserialization to a list of allowed types will not resolve this issue.
This behavior is intrinsic to BinaryFormatter
and cannot practically be addressed without changing both the serialized payload format itself and its supported capability set. And once we do that, what we have is essentially a brand new serializer incompatible with BinaryFormatter
. This would not help existing consumers of BinaryFormatter
.