Ensure NetDataContractSerializer.Binder is set before deserializing
Ensure NetDataContractSerializer.Binder is set before deserializing
Microsoft docsDescription
This rule finds System.Runtime.Serialization.NetDataContractSerializer deserialization method calls or references when the System.Runtime.Serialization.NetDataContractSerializer.Binder might be null. If you want to disallow any deserialization with System.Runtime.Serialization.NetDataContractSerializer regardless of the System.Runtime.Serialization.NetDataContractSerializer.Binder property, disable this rule and CA2311, and enable rule CA2310.
NetDataContractSerializer is insecure and can't be made secure. For more information, see the BinaryFormatter security guide.
Cause
A System.Runtime.Serialization.NetDataContractSerializer deserialization method was called or referenced and the System.Runtime.Serialization.NetDataContractSerializer.Binder property may be null.
This rule is similar to CA2311, but analysis can't determine if the System.Runtime.Serialization.NetDataContractSerializer.Binder is definitely null.
By default, this rule analyzes the entire codebase, but this is configurable. Restricting types with a SerializationBinder can't prevent all attacks. For more information, see the BinaryFormatter security guide.
Example
using System;
using System.IO;
using System.Runtime.Serialization;
public class BookRecordSerializationBinder : SerializationBinder
{
public override 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")
{
return typeof(BookRecord);
}
else if (typeName == "AisleLocation")
{
return typeof(AisleLocation);
}
else
{
throw new ArgumentException("Unexpected type", nameof(typeName));
}
}
}
[DataContract]
public class BookRecord
{
[DataMember]
public string Title { get; set; }
[DataMember]
public AisleLocation Location { get; set; }
}
[DataContract]
public class AisleLocation
{
[DataMember]
public char Aisle { get; set; }
[DataMember]
public byte Shelf { get; set; }
}
public class Binders
{
public static SerializationBinder BookRecord = new BookRecordSerializationBinder();
}
public class ExampleClass
{
public BookRecord DeserializeBookRecord(byte[] bytes)
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
serializer.Binder = Binders.BookRecord;
using (MemoryStream ms = new MemoryStream(bytes))
{
return (BookRecord) serializer.Deserialize(ms);
}
}
}
using System;
using System.IO;
using System.Runtime.Serialization;
public class BookRecordSerializationBinder : SerializationBinder
{
public override 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")
{
return typeof(BookRecord);
}
else if (typeName == "AisleLocation")
{
return typeof(AisleLocation);
}
else
{
throw new ArgumentException("Unexpected type", nameof(typeName));
}
}
}
[DataContract]
public class BookRecord
{
[DataMember]
public string Title { get; set; }
[DataMember]
public AisleLocation Location { get; set; }
}
[DataContract]
public class AisleLocation
{
[DataMember]
public char Aisle { get; set; }
[DataMember]
public byte Shelf { get; set; }
}
public class Binders
{
public static SerializationBinder BookRecord = new BookRecordSerializationBinder();
}
public class ExampleClass
{
public BookRecord DeserializeBookRecord(byte[] bytes)
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
// Ensure that Binder is always non-null before deserializing
serializer.Binder = Binders.BookRecord ?? throw new Exception("Expected non-null");
using (MemoryStream ms = new MemoryStream(bytes))
{
return (BookRecord) serializer.Deserialize(ms);
}
}
}
using System;
using System.IO;
using System.Runtime.Serialization;
[DataContract]
public class BookRecord
{
[DataMember]
public string Title { get; set; }
[DataMember]
public string Author { get; set; }
[DataMember]
public int PageCount { get; set; }
[DataMember]
public AisleLocation Location { get; set; }
}
[DataContract]
public class AisleLocation
{
[DataMember]
public char Aisle { get; set; }
[DataMember]
public byte Shelf { get; set; }
}
public class ExampleClass
{
public NetDataContractSerializer Serializer { get; set; }
public BookRecord DeserializeBookRecord(byte[] bytes)
{
using (MemoryStream ms = new MemoryStream(bytes))
{
return (BookRecord) this.Serializer.Deserialize(ms); // CA2312 violation
}
}
}
using System;
using System.IO;
using System.Runtime.Serialization;
public class BookRecordSerializationBinder : SerializationBinder
{
public override 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")
{
return typeof(BookRecord);
}
else if (typeName == "AisleLocation")
{
return typeof(AisleLocation);
}
else
{
throw new ArgumentException("Unexpected type", nameof(typeName));
}
}
}
[DataContract]
public class BookRecord
{
[DataMember]
public string Title { get; set; }
[DataMember]
public string Author { get; set; }
[DataMember]
public int PageCount { get; set; }
[DataMember]
public AisleLocation Location { get; set; }
}
[DataContract]
public class AisleLocation
{
[DataMember]
public char Aisle { get; set; }
[DataMember]
public byte Shelf { get; set; }
}
public class ExampleClass
{
public BookRecord DeserializeBookRecord(byte[] bytes)
{
NetDataContractSerializer serializer = new NetDataContractSerializer();
serializer.Binder = new BookRecordSerializationBinder();
using (MemoryStream ms = new MemoryStream(bytes))
{
return (BookRecord) serializer.Deserialize(ms);
}
}
}When to suppress
NetDataContractSerializer is insecure and can't be made secure.