When an object is created via deserialization, its constructor is often not executed: the object is instead built directly from its serialized
data.
This means an attacker can create a malicious object and completely bypass security checks.
Ask Yourself Whether
- Constructors of this
Serializable class lack relevant security checks.
- Security checks performed during deserialization differ from those in the constructor.
There is a risk if you answered yes to any of those questions.
Recommended Secure Coding Practices
- Ensure validation is applied to all entry points of an application.
- Ensure that validation is identical regardless of how an object gets created.
Sensitive Code Example
In the following examples, Serializable classes either omit validation or apply different validation logic during instantiation than
during deserialization.
For classes inheriting ISerializable:
[Serializable]
public class InternalUrl : ISerializable
{
private string url;
public InternalUrl(string tmpUrl)
{
if(!tmpUrl.StartsWith("http://localhost/"))
{
url = "http://localhost/default";
}
else
{
url = tmpUrl;
}
}
// Special Deserialization constructor
protected InternalUrl(SerializationInfo info, StreamingContext context)
{
url = (string) info.GetValue("url", typeof(string));
// Sensitive - no validation
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("url", url);
}
}
For classes inheriting IDeserializationCallback:
[Serializable]
public class InternalUrl : IDeserializationCallback
{
private string url;
public InternalUrl(string tmpUrl)
{
if(!tmpUrl.StartsWith("http://localhost/"))
{
url = "http://localhost/default";
}
else
{
url = tmpUrl;
}
}
void IDeserializationCallback.OnDeserialization(object sender)
{
// Sensitive - no validation
}
}
For classes inheriting from neither of previous types:
[Serializable]
public class InternalUrl
{
private string url;
public InternalUrl(string tmpUrl)
{
// Sensitive - no validation
url = tmpUrl;
}
}
Compliant Solution
For classes inheriting ISerializable:
[Serializable]
public class InternalUrl : ISerializable
{
private string url;
public InternalUrl(string tmpUrl)
{
url = tmpUrl;
validate();
}
// Special Deserialization constructor
protected InternalUrl(SerializationInfo info, StreamingContext context)
{
url = (string) info.GetValue("url", typeof(string));
validate();
}
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("url", url);
}
void validate()
{
if(!url.StartsWith("http://localhost/"))
{
url = "http://localhost/default";
}
}
}
For classes inheriting IDeserializationCallback:
[Serializable]
public class InternalUrl : IDeserializationCallback
{
private string url;
public InternalUrl(string tmpUrl)
{
url = tmpUrl;
validate();
}
void IDeserializationCallback.OnDeserialization(object sender)
{
validate();
}
}
For classes inheriting from neither of previous types:
[Serializable]
public class InternalUrl
{
private string url;
public InternalUrl(string tmpUrl)
{
if(!tmpUrl.StartsWith("http://localhost/"))
{
url = "http://localhost/default";
}
else
{
url = tmpUrl;
}
}
}
See