Why is this an issue?
During the deserialization process, the state of an object will be reconstructed from the serialized data stream which can contain dangerous
operations.
For example, a well-known attack vector consists in serializing an object of type TempFileCollection
with arbitrary files (defined by an attacker) which will be deleted on the application deserializing this object (when the finalize() method of
the TempFileCollection object is called). This kind of types are called "gadgets".
Instead of using BinaryFormatter
and similar serializers, it is recommended to use safer alternatives in most of the cases, such as XmlSerializer or DataContractSerializer. If
it’s not possible then try to mitigate the risk by restricting the types allowed to be deserialized:
- by implementing an "allow-list" of types, but keep in mind that novel dangerous types are regularly discovered and this protection could be
insufficient over time.
- or/and implementing a tamper protection, such as message authentication codes (MAC). This way
only objects serialized with the correct MAC hash will be deserialized.
Noncompliant code example
For BinaryFormatter,
NetDataContractSerializer,
SoapFormatter
serializers:
var myBinaryFormatter = new BinaryFormatter();
myBinaryFormatter.Deserialize(stream); // Noncompliant: a binder is not used to limit types during deserialization
JavaScriptSerializer
should not use SimpleTypeResolver or other weak resolvers:
JavaScriptSerializer serializer1 = new JavaScriptSerializer(new SimpleTypeResolver()); // Noncompliant: SimpleTypeResolver is unsecure (every types is resolved)
serializer1.Deserialize<ExpectedType>(json);
LosFormatter should not be used without
MAC verification:
LosFormatter formatter = new LosFormatter(); // Noncompliant
formatter.Deserialize(fs);
Compliant solution
BinaryFormatter,
NetDataContractSerializer
, SoapFormatter
serializers should use a binder implementing a whitelist approach to limit types during deserialization (at least one exception should be thrown or a
null value returned):
sealed class CustomBinder : SerializationBinder
{
public override Type BindToType(string assemblyName, string typeName)
{
if (!(typeName == "type1" || typeName == "type2" || typeName == "type3"))
{
throw new SerializationException("Only type1, type2 and type3 are allowed"); // Compliant
}
return Assembly.Load(assemblyName).GetType(typeName);
}
}
var myBinaryFormatter = new BinaryFormatter();
myBinaryFormatter.Binder = new CustomBinder();
myBinaryFormatter.Deserialize(stream);
JavaScriptSerializer
should use a resolver implementing a whitelist to limit types during deserialization (at least one exception should be thrown or a null value
returned):
public class CustomSafeTypeResolver : JavaScriptTypeResolver
{
public override Type ResolveType(string id)
{
if(id != "ExpectedType") {
throw new ArgumentNullException("Only ExpectedType is allowed during deserialization"); // Compliant
}
return Type.GetType(id);
}
}
JavaScriptSerializer serializer = new JavaScriptSerializer(new CustomSafeTypeResolver()); // Compliant
serializer.Deserialize<ExpectedType>(json);
LosFormatter serializer with MAC
verification:
LosFormatter formatter = new LosFormatter(true, secret); // Compliant
formatter.Deserialize(fs);
Resources