As you may be aware, Microsoft published their paper titled
BinaryFormatter Obsoletion Strategy
a while ago, detailing exactly how they intend to phase out the
BinaryFormatter
for .NET 5.0 and later. Quoting
directly from the paper:
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.
With the availability of .NET 8, the fourth step of the
timeline has been reached in November 2023, which leaves only
the final removal BinaryFormatter
infrastructure
for .NET 9 next year. At DevExpress, we have followed
developments since the paper was first published, and taken
steps to make sure our codebase is up to date and our customers
needn’t concern themselves with the details of required
migration work. In this post I’d like to summarize what we did
and what remains to be done.
We carefully check for serializability
Binary deserialization is a concept that has been a deeply
integrated part of the Base Class Library (BCL) since the days
of the early .NET Framework. Many types required special
handling over the years, and the property
Type.IsSerializable
was historically used to determine whether a type could be
serialized or not.
As part of our security strategy (read a summary here), we created our own abstraction layer to encapsulate all
operations involving types and their resolution. The main entry
point to that layer is a class called
SafeTypeResolver
, and our codebase makes extensive
use of it today.
To cover aspects of serialization and deserialization, we added
special handling to this layer, and we introduced safe
alternatives for now-obsolete APIs like
Type.IsSerializable
. Since our code targets many
different versions of .NET Framework, .NET Core/Standard and
.NET, it was important to us to centralize serialization
decisions in one secure location.
We conditionally removed serialization constructors from
exception types
Custom exception types have included serialization constructors
since the first .NET Framework versions. To disable this
mechanism, our exception types now don’t include such
constructors anymore:
#if !NET
namespace DevExpress.DataAccess.Json {
using System.Runtime.Serialization;
[System.Serializable]
partial class JsonDataSourceException {
protected JsonDataSourceException(SerializationInfo info, StreamingContext context)
: base(info, context) {
}
}
}
#endif
Microsoft documents the NET
symbol on the page
Target Frameworks, Preprocessor symbols, to apply to .NET 5+ as well as .NET Core. However, our
current version codebase has been moved to .NET 6+ starting
with our 23.2 branch, so NET
technically refers
only to .NET 6+ for DevExpress code.
Some technologies and techniques used by our controls, as well
as by standard platform controls introduced by Microsoft, rely
on serialization mechanisms. Logically, binary serialization
will always need to occur in some places — think about
persisting pixel image data with a form definition, for
instance.
We implemented a safe formatter called
DXBinaryFormatter
to cover these use cases. It is
backwards compatible with the deprecated
BinaryFormatter
, but it does not include
implementations of the “dangerous” operations that led to
security concerns — the standard
BinaryFormatter
was trying to do too much for its
own good! Specifically, DXBinaryFormatter
does not
support (de)serialization of delegates and methods, or
techniques related to surrogates, Remoting, and COM proxies. Of
course this means that our new implementation is not a full
replacement, and it’s not meant to be that.
The DXBinaryFormatter
is an integral component of
our secure infrastructure, designed to ensure type and content
safety when working with binary data in any save/restore
functionality provided by our components. Several classes use
the DXBinaryFormatter
, including these:
-
The
SafeBinaryFormatter
selects the correct
formatter implementation to use based on runtime
circumstances. It also handles all type resolution requests
during deserialization, using the
SafeTypeResolver
mentioned previously. “Allow”
and “block” lists are used to guarantee that serialization
occurs only where intended.
-
The
BinaryTypeConverter
and some of its
descendant types use the SafeBinaryFormatter
to
serialize and deserialize standard types like
SvgImage
.
Note: We don’t plan to make these types
publicly available. We are cautious not to introduce any new
security concerns, and we cannot make promises about the use of
these helpers with arbitrary custom types. Our implementation
can also change at any time without notice.
DevExpress
security guidance for safe deserialization
explains the use of the two methods
RegisterTrustedClass
and
RegisterTrustedAssembly
on the type
DeserializationSettings
. These helpers add to the
“allow” list used by the
SafeBinaryFormatter
mentioned above. On the same
page, you’ll find information about the
BindToTypePolicy
, which enables you to control
precisely how assemblies and types are allowed to load
dynamically, in cases where static registration of trusted
types and assemblies is insufficient.
The method InvokeTrusted
, also explained on that
page, skips the checks of “allow” and “block” lists normally
performed but he SafeBinaryFormatter
.
Note that the methods of the
DeserializationSettings
discussed here do not
revert back to the deprecated
BinaryFormatter
under any circumstances.
Our build pipeline analyses both code and generated IL of
release assemblies
To be absolutely sure that we don’t suffer some kind of
regression and accidentally deploy code to customers that uses
unsafe serialization mechanisms, we have automated checks in
source code and IL which run in our build pipeline. At the time
of writing, the following list of types and APIs is checked and
flagged as necessary:
BinaryFormatter
type,
IFormatter
interface,
Formatter
type,
-
IFormatterConverter
interface and
FormatterConverter
type,
FormatterServices
static class,
IObjectReference
interface,
-
ISurrogateSelector
and
ISerializationSurrogate
interfaces, and the
SurrogateSelector
type,
-
ISafeSerializationData
and
SafeSerializationEventArgs
,
StreamingContextStates
enum,
-
ObjectIDGenerator
,ObjectManager
,
SerializationObjectManager
types,
-
FormatterTypeStyle
,
FormatterAssemblyStyle
, and
TypeFilterLevel
enums,
IFieldInfo
interface,
-
Type.IsSerializable
public property (and all
overridden implementations),
TypeAttributes.Serializable
enum value,
-
FieldInfo.IsNotSerialized
public property,
FieldAttributes.NotSerialized
enum value,
-
ISerializable.GetObjectData
method, but not the
type itself,
-
IObjectReference.GetRealObject
method, but not
the type itself,
-
SerializationInfo
and
StreamingContext
, all constructors
If you would like to check your own application source code in
a similar way, a good recommendation is to begin with Visual
Studio diagnostics. Specially, the items
SYSLIB0050
and
SYSLIB0051
can be useful since they flag the use of APIs declared obsolete
in .NET 8. IL-based static analysis can provide extra security.
We provide safe alternatives for the ImageList component
If your application uses the Windows Forms
ImageList
component, you will see exceptions like
this when the image list content in deserialized from
resources.
System.NotSupportedException: ‘BinaryFormatter serialization
and deserialization are disabled within this application. See
https://aka.ms/binaryformatter
for more information.’
If this applies to you, there will be code in your designer
files that looks like this:
imageList1.ColorDepth = ColorDepth.Depth8Bit;
imageList1.ImageStream = (ImageListStreamer)resources.GetObject("imageList1.ImageStream");
imageList1.TransparentColor = Color.Transparent;
This issue
in the .NET WinForms repository on GitHub shows that the
problem is not specific to applications that are technically
Windows Forms applications — the use of certain references is
enough to cause the exception. As described in that issue, you
could use the temporary workaround (that will go away with .NET
9, according to plans) of setting
EnableUnsafeBinaryFormatterSerialization
to
true
in the project file.
A better option however is to migrate to the DevExpress
components
ImageCollection
or
SvgImageCollection
. These implement safe serialization mechanisms and will
remain compatible when .NET 9 arrives.
We enable you to selectively opt in to safe serialization
If you decide — or have decided in the past — to run your
application using the
EnableUnsafeBinaryFormatterSerialization
project
setting, you can utilize helpers on the
DeserializationSettings
type to selectively run
with the safe DevExpress serialization mechanisms instead.
To switch on use of the DXBinaryFormatter
, you can
call this (in startup code, like Program.cs
:
DeserializationSettings.ForceDXBinaryFormatter();
Alternatively, you can force the use of the
DXBinaryFormatter
for a block of code:
DeserializationSettings.InvokeWithForcedDXBinaryFormatter(
() => {
// some critical code
});
Of course, based on Microsoft’s published plans, this option is
likely to become irrelevant when .NET 9 arrives, because
EnableUnsafeBinaryFormatterSerialization
should go
away then.
Note that the enforcement only applies to DevExpress code. If
you trigger execution of the deprecated
BinaryFormatter
in some other code path, these
methods will not make a difference.
We enable you to run without binary serialization
Most DevExpress components have seen changes in the last few
years that implement text-based persistence mechanisms to
replace the older binary serialization approaches. The
now-deprecated standard .NET Framework system is therefore
becoming less relevant, and your application probably uses new
text serialization already.
Note that on occasion such changes have led to breaking
changes. Here is
one random example
for column views. As always, we documented everything we
changed, especially where breaking changes occurred. Please get
in touch with us if you are concerned about any specific use
case, or the status of a control you use.
In some cases, applications may be able to work entirely
without binary serialization. It is possible to call this
helper on startup to disable binary serialization for your
entire project:
DeserializationSettings.DenyBinaryFormatter();
Alternatively, you can use selectively disable binary
serialization for a block of code:
DeserializationSettings.InvokeWithBinaryFormatterDenied(
() => {
// some critical code
});
Note that, again, this does not mean that we revert to the
deprecated BinaryFormatter
under any
circumstances. Technically these directives simply make any
binary serialization process return an empty or null object. As
long as your code uses DevExpress controls that do not rely on
binary serialization, it is safe to disable it completely. You
need to make this decision for your own projects.
What remains?
At this time there are a few points that have not been finally
decided by Microsoft — we need to wait for .NET 9 to arrive
before we know exactly which steps are required.
Specifically, the drag & drop related types
System.Windows.DataObject
and
System.Windows.Forms.DataObject
rely on the
deprecated BinaryFormatter
, and so does
System.Windows.Forms.AxHost
(COM inter) and even
the System.Data.DataSet
. The latter has
elaborate security guidance
already, and you can find GitHub issues relating to the other
types. Some work will certainly be done by the time .NET 9
comes around, if the obsoletion plans are followed as intended.
Drag & drop is an area that concerns us at DevExpress, but
we can’t act before Microsoft proposes plans from their end.
Clipboard functionality is a second similar area, where we have
reviewed clipboard operations in our codebase and prepared a
foundation and compatibility layer to accommodate future
changes Microsoft proposes. We are watching
GitHub issues like this one
for updates. Please read
this section about our new Clipboard Access Policy
related to this topic.
We will keep you posted as we move closer to the .NET 9
release!
Free DevExpress Products – Get Your Copy Today
The following free DevExpress product offers remain available. Should you have any questions about the free offers below, please submit a ticket via the
DevExpress Support Center at your convenience. We’ll be happy to follow-up.