Secure GetObjectData overrides
TypeName |
SecureGetObjectDataOverrides |
CheckId |
CA2110 |
Category |
Microsoft.Security |
Breaking Change |
Breaking |
A method is named GetObjectData
, is declared in a type that implements the System.Runtime.Serialization.ISerializable interface, and takes a System.Runtime.Serialization.SerializationInfo object and a System.Runtime.Serialization.StreamingContext object as parameters. The method is not protected by a demand for System.Security.Permissions.SecurityPermissionAttribute.SerializationFormatter security permission.
The System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext) method is declared in the ISerializable interface. This interface is implemented by types that provide custom serialization logic. GetObjectData is protected by a security check for System.Security.Permissions.SecurityPermissionAttribute.SerializationFormatter security permission. If an implementation of GetObjectData is not protected by the same security check, callers can call the implementation to bypass security on the interface and gain access to data serialized by the type.
To fix a violation of this rule, apply the following attribute to the GetObjectData
method:
[SecurityPermissionAttribute(SecurityAction.Demand,SerializationFormatter=true)]
.
Do not exclude a warning from this rule.
The following code shows a library that violates the rule and an application that exploits the library's weakness.
using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Security;
using System.Security.Permissions;
namespace SecurityRulesLibrary
{
[Serializable]
public class MySerializableObject :ISerializable
{
private int n1;
private DateTime date;
private string str;
private MySerializableObject (){}
public MySerializableObject (string s, int num1)
{
n1 = num1;
str = (s == null ? "<empty>" : s);
// This value is intended to be private and unviewable.
date = DateTime.Today;
}
public MySerializableObject(SerializationInfo info, StreamingContext context)
{
n1 = (int) info.GetValue("n1", typeof(int));
date = (DateTime) info.GetValue("date", typeof(DateTime));
str = (string) info.GetValue("str", typeof(string));
}
// The ISerializable interface member that provides the serialization logic.
// Violates rule: GetObjectDataRequiresSerializationFormatterSecurityPermissionAttribute.
public void GetObjectData(SerializationInfo info, StreamingContext context)
{
info.AddValue("n1", n1);
info.AddValue("date", date);
info.AddValue("str", str);
}
}
}
The following code exploits the library's weakness.
using System;
using System.Security;
using System.Security.Permissions;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;
[assembly: SecurityPermissionAttribute(
SecurityAction.RequestRefuse, SerializationFormatter = true)]
namespace TestSecurityLibrary
{
public class TestGetObjectData
{
int n1;
DateTime date;
string str;
SerializationInfo info = new SerializationInfo(
typeof(MySerializableObject),
new FormatterConverter());
StreamingContext context = new StreamingContext(
StreamingContextStates.All);
private void GetInformation(SerializationInfo info)
{
// Use the SerializationInfo to look at all of
// the serialized object's private data.
n1 = info.GetInt32("n1");
date = info.GetDateTime("date");
str = info.GetString("str");
}
private void GetTheObjectDirectly(MySerializableObject obj)
{
// Bypasses security that protects the interface.
obj.GetObjectData(info, context);
GetInformation(info);
Console.WriteLine("Directly: {0}, {1}, {2}", str, n1, date);
}
private void GetTheObjectThroughInterface(MySerializableObject obj)
{
// This call causes a security exception in a type that does
// not have SerializationFormatter security permission.
((ISerializable)obj).GetObjectData(info, context);
GetInformation(info);
Console.WriteLine(
"ISerializable: {0}, {1}, {2}", str, n1, date);
}
public static void Main()
{
// Get an instance of the object.
MySerializableObject anObject =
new MySerializableObject("test", 1);
// Call GetObjectData directly and
// then call through the interface.
TestGetObjectData getDirect = new TestGetObjectData();
// Succeeds - Bypasses the security on the interface.
getDirect.GetTheObjectDirectly(anObject);
TestGetObjectData getIndirect = new TestGetObjectData();
try
{
// Fails - Shows the security on the interface.
getIndirect.GetTheObjectThroughInterface(anObject);
}
catch (SecurityException e)
{
Console.WriteLine(
"Object cannot be accessed through the interface - {0}",
e.Message);
}
}
}
}
This example produces the following output.
Directly: test, 1, 7/30/2002 12:00:00 AM Object cannot be accessed through the interface - Request for the permission of type System.Security.Permissions.SecurityPermission, mscorlib, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 failed.
Secure serialization constructors
Implement serialization constructors
Mark ISerializable types with serializable
Override link demands should be identical to base
System.Runtime.Serialization.ISerializable
System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo,System.Runtime.Serialization.StreamingContext)
System.Runtime.Serialization.SerializationInfo
System.Runtime.Serialization.StreamingContext
System.Security.Permissions.SecurityPermissionAttribute.SerializationFormatter