generalinformation-404441-security-safe-deserialization.md
DevExpress controls use a safety mechanism for all deserialization operations to improve app security. An exception is thrown if a control attempts to load an unsafe type.
You should review all unsafe type exceptions. If you trust a certain exception type, use the following code to enable deserialization:
DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(typeof(CustomClass));
DevExpress.Utils.DeserializationSettings.RegisterTrustedClass(GetType(CustomClass))
Call the following method to trust all exception types from a specific assembly:
DevExpress.Utils.DeserializationSettings.RegisterTrustedAssembly("CustomAssembly, Version=x.x.x.x, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx");
DevExpress.Utils.DeserializationSettings.RegisterTrustedAssembly(typeof(CustomClass).Assembly);
DevExpress.Utils.DeserializationSettings.RegisterTrustedAssembly("CustomAssembly, Version=x.x.x.x, Culture=neutral, PublicKeyToken=xxxxxxxxxxxxxxxx")
DevExpress.Utils.DeserializationSettings.RegisterTrustedAssembly(GetType(CustomClass).Assembly)
If you trust a data source that raised a given security exception, you can turn off safe deserialization for certain sections of code:
DevExpress.Utils.DeserializationSettings.InvokeTrusted(()=>{
// Trusted deserialization.
// gridView1.RestoreLayoutFromXml(fileName);
});
DevExpress.Utils.DeserializationSettings.InvokeTrusted(
Sub()
' Trusted deserialization.
' GridView1.RestoreLayoutFromXml(fileName)
End Sub
)
DevExpress UI controls may call methods such as Assembly.Load or Type.GetType() internally, to load assemblies and data types dynamically. Use the DevExpress.Utils.BindToTypePolicy if you require greater control over assembly/type loading. The policy allows you to inspect assemblies/types and cancel the load operation or type resolution based on a specific condition.
The policy raises the BindToTypePolicy.QueryAssemblyLoad event when DevExpress UI controls load assemblies. Handle this event to spot an “unknown” assembly before it is loaded, check its name, and allow or cancel the operation.
The following example demonstrates how to load a specific version from multiple versions of an assembly:
void BindToTypePolicy_QueryAssemblyLoad(object sender, BindToTypePolicy.QueryAssemblyLoadEventArgs e) {
if(e.AssemblyName.StartsWith("EntityFramework", StringComparison.OrdinalIgnoreCase)) {
// Loads a specific assembly version distributed with the application.
var appDirectory = Path.GetDirectoryName(typeof(Program).Assembly.Location);
string path = Path.Combine(appDirectory, @"..\\Lib", "EntityFramework.dll");
e.Assembly = Assembly.LoadFrom(path);
}
}
Event parameters include:
e.Cancel – Set this parameter to true to cancel the operation.Note
You can also call the following methods at application startup to apply a restrictive policy:
BindToTypePolicy.DenyAssemblyLoading – Prevents dynamic loading of assemblies by DevExpress UI elements.
BindToTypePolicy.DenyAssemblyLoadingFromFilesAndBytes – Prevents DevExpress UI controls from loading assemblies from arbitraty paths and Byte arrays (Assembly.LoadFile(String), Assembly.Load(Byte[])).
static void Main() {
DevExpress.Data.BindToTypePolicy.DenyAssemblyLoadingFromFilesAndBytes();
Application.Run(new Form1());
}
Shared Sub Main()
DevExpress.Data.BindToTypePolicy.DenyAssemblyLoadingFromFilesAndBytes()
Application.Run(New Form1())
End Sub
Handle the BindToTypePolicy.QueryBindToType event to spot dynamic loading of a type, check its name and assembly, and allow the type resolve operation. The policy fires this event every time DevExpress UI controls call the Type.GetType() method.
void BindToTypePolicy_QueryBindToType(object sender, BindToTypePolicy.QueryBindToTypeEventArgs e) {
if(!e.IsKnownType) {
// Resolves custom datasource types for DevExpress Reports.
if(e.TypeName == "ProductsJsonDataSource")
e.Type = typeof(ProductsJsonDataSource);
if(e.TypeName == "UsersJsonDataSource")
e.Type = typeof(UsersJsonDataSource);
}
}
Event parameters include:
Type object is being loaded.e.Cancel – Set this parameter to true to cancel type resolution.Note
Handle the BindToTypePolicy.QueryNonTrustedTypeValidation event to validate blacklisted and “unknown” types. The policy does not fire this event to whitelisted/trusted types.
static void BindToTypePolicy_QueryNonTrustedTypeValidation(object sender, BindToTypePolicy.QueryNonTrustedTypeValidationEventArgs e) {
if(e.IsUnsafe)
throw new MyAppLicationSecurityException(e.AssemlyQualifiedTypeName);
if(e.AssemblyName == typeof(Program).Assembly.FullName)
e.TrustThisType();
if(e.TypeName == "ObsoleteJsonDataSource")
e.DoNotTrustThisType();
}
void BindToTypePolicy_QueryAssemblyLoad(object sender, BindToTypePolicy.QueryAssemblyLoadEventArgs e) {
Console.WriteLine(e.ToString());
}
void BindToTypePolicy_QueryBindToType(object sender, BindToTypePolicy.QueryBindToTypeEventArgs e) {
Console.WriteLine(e.ToString());
}
DevExprress UI controls serialize and deserialize System.Object properties if properties are marked with the XtraSerializableProperty attribute (for example, the Tag property) and contain simple data types (primitive data type, string, decimal, DateTime, Guid, and nullable variants of these types). System.Object properties that contain custom type values are not (de)serialized automatically.
(De)Serialization for a custom type is only possible for a string. You can implement a converter (use the IOneTypeObjectConverter interface) that converts a custom type to/from a string. The converter is invoked automatically whenever our (de)serialization mechanism encounters a registered custom type.
The following example demonstrates how to create a simple type converter and register it at application startup:
using DevExpress.Utils.Serializing.Helpers;
struct CustomType {
public readonly int Value;
public CustomType(int value) {
this.Value = value;
}
}
public class CustomTypeConverter : IOneTypeObjectConverter {
public Type Type {
get { return typeof(CustomType); }
}
public string ToString(object obj) {
return ((CustomType)obj).Value.ToString("D");
}
public object FromString(string str) {
return new CustomType(int.Parse(str));
}
}
// Register the converter at application startup.
ObjectConverter.Instance.RegisterConverter(new CustomTypeConverter());
Imports DevExpress.Utils.Serializing.Helpers
Friend Structure CustomType
Public ReadOnly Value As Integer
Public Sub New(ByVal value As Integer)
Me.Value = value
End Sub
End Structure
Public Class CustomTypeConverter
Implements IOneTypeObjectConverter
Public ReadOnly Property Type As Type Implements IOneTypeObjectConverter.Type
Get
Return GetType(CustomType)
End Get
End Property
Public Shadows Function ToString(obj As Object) As String Implements IOneTypeObjectConverter.ToString
Return (CType(obj, CustomType)).Value.ToString("D")
End Function
Public Function FromString(str As String) As Object Implements IOneTypeObjectConverter.FromString
Return New CustomType(Integer.Parse(str))
End Function
End Class
' Register the converter at application startup.
ObjectConverter.Instance.RegisterConverter(New CustomTypeConverter())