I'm facing a strange issue with WCF serialization. My project has a type named RefEntity
which has a property of another type named RefDomain
. A WCF service defines two methods, one takes type RefEntity
as parameter, and another takes type RefDomain
as parameter.
The method with the RefDomain
parameter doesn't seem to serialize the object as all the properties have null
value on the service side. Whereas, the method with RefEntity
parameter (and RefDomain
as a member) works as expected (RefDomain
member is serialized as expected).
What am I missing here?
public class RefDomain
{
private string name;
private RefDomain parent;
private Type entityType;
public string Name
{
get => this.name;
protected set => this.name = value;
}
public RefDomain Parent
{
get => this.parent;
protected set => this.parent = value;
}
public Type RefEntityType
{
get => this.entityType;
protected set => this.entityType = value;
}
}
CodePudding user response:
Your properties have protected setters, and DataContractSerializer
will only serialize properties that are fully public, unless marked with data contract attributes.
Once you apply the necessary [DataContract]
and [DataMember]
attributes, you will discover a second problem, namely that DataContractSerializer
cannot serialize values of type System.Type
. (Demo fiddle #1 here). To serialize your Type RefEntityType
property, you will need to use some sort of surrogate for Type
that can be serialized, for instance a string.
Thus the following version of RefDomain
can be serialized via the data contract serializer:
public class RefDomain
{
private string name;
private RefDomain parent;
private Type entityType;
[DataMember]
public string Name
{
get => this.name;
protected set => this.name = value;
}
[DataMember]
public RefDomain Parent
{
get => this.parent;
protected set => this.parent = value;
}
public Type RefEntityType
{
get => this.entityType;
protected set => this.entityType = value;
}
[DataMember(Name = "RefEntityType")]
string RefEntityTypeString
{
get
{
return RefEntityType?.FullName;
}
set
{
// Note that simply deserializing a type supplied over the wire will make your application vulnerable to type injection attacks,
// in which an attacker tricks you into constructing a type that effects an attack when constructed, deserialized or disposed.
// This is a known vulnerability with Json.NET's TypeNameHandling.None. See for details
// https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf
// https://stackoverflow.com/questions/39565954/typenamehandling-caution-in-newtonsoft-json/
// https://stackoverflow.com/questions/49038055/external-json-vulnerable-because-of-json-net-typenamehandling-auto
var type = value == null ? null : Type.GetType(value);
// Check the type against a whitelist here?
RefEntityType = type;
}
}
// Your class was had no way to set the protected properties, so I added a parameterized constructor.
public RefDomain() { }
public RefDomain(string name, RefDomain parent, Type entityType) => (this.name, this.parent, this.entityType) = (name, parent, entityType);
}
Notes:
Deserializing a type supplied over the wire will make your application vulnerable to attack gadget deserialization attacks, in which an attacker tricks your app into constructing a type that effects an attack when constructed, deserialized or disposed. This is a known vulnerability with Json.NET's
TypeNameHandling
. See for details:- https://www.blackhat.com/docs/us-17/thursday/us-17-Munoz-Friday-The-13th-JSON-Attacks-wp.pdf
- TypeNameHandling caution in Newtonsoft Json
- External json vulnerable because of Json.Net TypeNameHandling auto?
If your app ever constructs an instance of your
RefEntityType
, you app may become vulnerable to such attacks. To prevent this, you could check the deserialized type against a whitelist of allowed types.
Demo fiddle #2 here.
CodePudding user response:
As far as I know, user-defined types used in wcf need to be tagged with the Data Contract attribute, and members need to be tagged with the Data Member attribute.
You can either mark RefDomain as a Data Contract attribute or create a separate class to hold data from RefDomain and pass it through wcf.