This rule is similar to CA2301, but analysis can't determine if the Binder is definitely null.
By default, this rule analyzes the entire codebase, but this is configurable.
Upozorenje
Restricting types with a SerializationBinder can't prevent all attacks. For more information, see the BinaryFormatter security guide.
Rule description
Insecure deserializers are vulnerable when deserializing untrusted data. An attacker could modify the serialized data to include unexpected types to inject objects with malicious side effects. An attack against an insecure deserializer could, for example, execute commands on the underlying operating system, communicate over the network, or delete files.
Use a secure serializer instead, and don't allow an attacker to specify an arbitrary type to deserialize. For more information see the Preferred alternatives.
Make the serialized data tamper-proof. After serialization, cryptographically sign the serialized data. Before deserialization, validate the cryptographic signature. Protect the cryptographic key from being disclosed and design for key rotations.
This option makes code vulnerable to denial of service attacks and possible remote code execution attacks in the future. For more information, see the BinaryFormatter security guide. Restrict deserialized types. Implement a custom System.Runtime.Serialization.SerializationBinder. Before deserializing, set the Binder property to an instance of your custom SerializationBinder in all code paths. In the overridden BindToType method, if the type is unexpected, throw an exception to stop deserialization.
When to suppress warnings
BinaryFormatter is insecure and can't be made secure.
Configure code to analyze
Use the following options to configure which parts of your codebase to run this rule on.
You can configure these options for just this rule, for all rules they apply to, or for all rules in this category (Security) that they apply to. For more information, see Code quality rule configuration options.
Exclude specific symbols
You can exclude specific symbols, such as types and methods, from analysis by setting the excluded_symbol_names option. For example, to specify that the rule should not run on any code within types named MyType, add the following key-value pair to an .editorconfig file in your project:
Replace the XXXX part of CAXXXX with the ID of the applicable rule.
Allowed symbol name formats in the option value (separated by |):
Symbol name only (includes all symbols with the name, regardless of the containing type or namespace).
Fully qualified names in the symbol's documentation ID format. Each symbol name requires a symbol-kind prefix, such as M: for methods, T: for types, and N: for namespaces.
.ctor for constructors and .cctor for static constructors.
Matches specific methods MyMethod1 and MyMethod2 with the respective fully qualified signatures.
Exclude specific types and their derived types
You can exclude specific types and their derived types from analysis by setting the excluded_type_names_with_derived_types option. For example, to specify that the rule should not run on any methods within types named MyType and their derived types, add the following key-value pair to an .editorconfig file in your project:
Matches specific types MyType1 and MyType2 with the respective fully qualified names, and all of their derived types.
Pseudo-code examples
Violation 1
C#
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
publicclassBookRecordSerializationBinder : SerializationBinder
{
publicoverride Type BindToType(string assemblyName, string typeName)
{
// One way to discover expected types is through testing deserialization// of **valid** data and logging the types used.////Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')");if (typeName == "BookRecord")
{
returntypeof(BookRecord);
}
elseif (typeName == "AisleLocation")
{
returntypeof(AisleLocation);
}
else
{
thrownew ArgumentException("Unexpected type", nameof(typeName));
}
}
}
[Serializable]
publicclassBookRecord
{
publicstring Title { get; set; }
public AisleLocation Location { get; set; }
}
[Serializable]
publicclassAisleLocation
{
publicchar Aisle { get; set; }
publicbyte Shelf { get; set; }
}
publicclassBinders
{
publicstatic SerializationBinder BookRecord =
new BookRecordSerializationBinder();
}
publicclassExampleClass
{
public BookRecord DeserializeBookRecord(byte[] bytes)
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Binder = Binders.BookRecord;
using (MemoryStream ms = new MemoryStream(bytes))
{
return (BookRecord)formatter.Deserialize(ms); // CA2302 violation
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
Public Class BookRecordSerializationBinder
Inherits SerializationBinder
Public Overrides Function BindToType(assemblyName As String, typeName As String) As Type
' One way to discover expected types is through testing deserialization
' of **valid** data and logging the types used.
'Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')")
If typeName = "BinaryFormatterVB.BookRecord" Then
Return GetType(BookRecord)
Else If typeName = "BinaryFormatterVB.AisleLocation" Then
Return GetType(AisleLocation)
Else
Throw New ArgumentException("Unexpected type", NameOf(typeName))
End If
End Function
End Class
<Serializable()>
Public Class BookRecord
Public Property Title As String
Public Property Location As AisleLocation
End Class
<Serializable()>
Public Class AisleLocation
Public Property Aisle As Char
Public Property Shelf As Byte
End Class
Public Class Binders
Public Shared Property BookRecord As SerializationBinder = New BookRecordSerializationBinder()
End Class
Public Class ExampleClass
Public Function DeserializeBookRecord(bytes As Byte()) As BookRecord
Dim formatter As BinaryFormatter = New BinaryFormatter()
formatter.Binder = Binders.BookRecord
Using ms As MemoryStream = New MemoryStream(bytes)
Return CType(formatter.Deserialize(ms), BookRecord) ' CA2302 violation
End Using
End Function
End Class
Solution 1
C#
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
publicclassBookRecordSerializationBinder : SerializationBinder
{
publicoverride Type BindToType(string assemblyName, string typeName)
{
// One way to discover expected types is through testing deserialization// of **valid** data and logging the types used.////Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')");if (typeName == "BookRecord")
{
returntypeof(BookRecord);
}
elseif (typeName == "AisleLocation")
{
returntypeof(AisleLocation);
}
else
{
thrownew ArgumentException("Unexpected type", nameof(typeName));
}
}
}
[Serializable]
publicclassBookRecord
{
publicstring Title { get; set; }
public AisleLocation Location { get; set; }
}
[Serializable]
publicclassAisleLocation
{
publicchar Aisle { get; set; }
publicbyte Shelf { get; set; }
}
publicclassBinders
{
publicstatic SerializationBinder BookRecord =
new BookRecordSerializationBinder();
}
publicclassExampleClass
{
public BookRecord DeserializeBookRecord(byte[] bytes)
{
BinaryFormatter formatter = new BinaryFormatter();
// Ensure that Binder is always non-null before deserializing
formatter.Binder = Binders.BookRecord ?? thrownew Exception("Expected non-null binder");
using (MemoryStream ms = new MemoryStream(bytes))
{
return (BookRecord)formatter.Deserialize(ms);
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
Public Class BookRecordSerializationBinder
Inherits SerializationBinder
Public Overrides Function BindToType(assemblyName As String, typeName As String) As Type
' One way to discover expected types is through testing deserialization
' of **valid** data and logging the types used.
'Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')")
If typeName = "BinaryFormatterVB.BookRecord" Then
Return GetType(BookRecord)
Else If typeName = "BinaryFormatterVB.AisleLocation" Then
Return GetType(AisleLocation)
Else
Throw New ArgumentException("Unexpected type", NameOf(typeName))
End If
End Function
End Class
<Serializable()>
Public Class BookRecord
Public Property Title As String
Public Property Location As AisleLocation
End Class
<Serializable()>
Public Class AisleLocation
Public Property Aisle As Char
Public Property Shelf As Byte
End Class
Public Class Binders
Public Shared Property BookRecord As SerializationBinder = New BookRecordSerializationBinder()
End Class
Public Class ExampleClass
Public Function DeserializeBookRecord(bytes As Byte()) As BookRecord
Dim formatter As BinaryFormatter = New BinaryFormatter()
' Ensure that Binder is always non-null before deserializing
formatter.Binder = If(Binders.BookRecord, New Exception("Expected non-null"))
Using ms As MemoryStream = New MemoryStream(bytes)
Return CType(formatter.Deserialize(ms), BookRecord)
End Using
End Function
End Class
Violation 2
C#
using System;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;
[Serializable]
publicclassBookRecord
{
publicstring Title { get; set; }
public AisleLocation Location { get; set; }
}
[Serializable]
publicclassAisleLocation
{
publicchar Aisle { get; set; }
publicbyte Shelf { get; set; }
}
publicclassExampleClass
{
public BinaryFormatter Formatter { get; set; }
public BookRecord DeserializeBookRecord(byte[] bytes)
{
using (MemoryStream ms = new MemoryStream(bytes))
{
return (BookRecord) this.Formatter.Deserialize(ms); // CA2302 violation
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization.Formatters.Binary
<Serializable()>
Public Class BookRecord
Public Property Title As String
Public Property Location As AisleLocation
End Class
<Serializable()>
Public Class AisleLocation
Public Property Aisle As Char
Public Property Shelf As Byte
End Class
Public Class ExampleClass
Public Property Formatter As BinaryFormatter
Public Function DeserializeBookRecord(bytes As Byte()) As BookRecord
Using ms As MemoryStream = New MemoryStream(bytes)
Return CType(Me.Formatter.Deserialize(ms), BookRecord) ' CA2302 violation
End Using
End Function
End Class
Solution 2
C#
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
publicclassBookRecordSerializationBinder : SerializationBinder
{
publicoverride Type BindToType(string assemblyName, string typeName)
{
// One way to discover expected types is through testing deserialization// of **valid** data and logging the types used.////Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')");if (typeName == "BookRecord")
{
returntypeof(BookRecord);
}
elseif (typeName == "AisleLocation")
{
returntypeof(AisleLocation);
}
else
{
thrownew ArgumentException("Unexpected type", nameof(typeName));
}
}
}
[Serializable]
publicclassBookRecord
{
publicstring Title { get; set; }
public AisleLocation Location { get; set; }
}
[Serializable]
publicclassAisleLocation
{
publicchar Aisle { get; set; }
publicbyte Shelf { get; set; }
}
publicclassExampleClass
{
public BookRecord DeserializeBookRecord(byte[] bytes)
{
BinaryFormatter formatter = new BinaryFormatter();
formatter.Binder = new BookRecordSerializationBinder();
using (MemoryStream ms = new MemoryStream(bytes))
{
return (BookRecord) formatter.Deserialize(ms);
}
}
}
Imports System
Imports System.IO
Imports System.Runtime.Serialization
Imports System.Runtime.Serialization.Formatters.Binary
Public Class BookRecordSerializationBinder
Inherits SerializationBinder
Public Overrides Function BindToType(assemblyName As String, typeName As String) As Type
' One way to discover expected types is through testing deserialization
' of **valid** data and logging the types used.
'Console.WriteLine($"BindToType('{assemblyName}', '{typeName}')")
If typeName = "BinaryFormatterVB.BookRecord" Then
Return GetType(BookRecord)
Else If typeName = "BinaryFormatterVB.AisleLocation" Then
Return GetType(AisleLocation)
Else
Throw New ArgumentException("Unexpected type", NameOf(typeName))
End If
End Function
End Class
<Serializable()>
Public Class BookRecord
Public Property Title As String
Public Property Location As AisleLocation
End Class
<Serializable()>
Public Class AisleLocation
Public Property Aisle As Char
Public Property Shelf As Byte
End Class
Public Class ExampleClass
Public Function DeserializeBookRecord(bytes As Byte()) As BookRecord
Dim formatter As BinaryFormatter = New BinaryFormatter()
formatter.Binder = New BookRecordSerializationBinder()
Using ms As MemoryStream = New MemoryStream(bytes)
Return CType(formatter.Deserialize(ms), BookRecord)
End Using
End Function
End Class
Izvor za ovaj sadržaj možete pronaći na GitHubu, gdje možete stvarati i pregledavati probleme i zahtjeve za povlačenjem. Dodatne informacije potražite u našem vodiču za suradnike.
Povratne informacije o proizvodu .NET
.NET je projekt otvorenog koda. Odaberite vezu za slanje povratnih informacija:
Pridružite se seriji susreta kako biste s kolegama programerima i stručnjacima izgradili skalabilna rješenja umjetne inteligencije temeljena na stvarnim slučajevima upotrebe.