1

I'm trying to build a sandbox using an app-domain to isolate execution of potentially bad code.

Among other things I'd like to restrict reflection.

I'm building the sandbox this way:

AppDomainSetup sandboxSetup = new AppDomainSetup
{
    ApplicationBase = "."
};
PermissionSet permissions = new PermissionSet(PermissionState.None);
permissions.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
AppDomain sandbox = AppDomain.CreateDomain("sandbox", null, sandboxSetup, permissions);

It's working fine with private instance fields and private properties: any attempts to access them in the sandbox is rejected by the runtime.

But I've noticed that it doesn't works with literal fields (const in C#): it's always possible to get the value of a literal field, even if private:

private const string PASSWORD = "secret";
private string password = "secret";
private string Password
{
    get
    {
        return "secret";
    }
}

password and Password are correctly protected but any code can get the value of PASSWORD with basic reflection:

string password = typeof(User).GetField("PASSWORD", BindingFlags.NonPublic | BindingFlags.Static).GetValue(currentUser) as string;  // OK no problem take it, it's free!

I'd like to understand the rationales behind this behavior: is it because a literal value would always be "easily" visible in an assembly so preventing reflection is a losing battle, or because a final value is not really "invoked" so there is no security check or...?

This example is not really relevant, because a password would not be shared, but imagine the secret value is a salt value used for cryptography or something like that...

Thanks for your help.

Pragmateek
  • 13,174
  • 9
  • 74
  • 108

1 Answers1

2

When you get a FieldInfo for the constant, you get the MdFieldInfo type. If we decompile the code, we will see that it does not do a security check for that type.

Here's decompiled code for the MdFieldInfo type:

[DebuggerHidden]
[DebuggerStepThrough]
[TargetedPatchingOptOut("Performance critical to inline this type of method across NGen image boundaries")]
public override object GetValue(object obj)
{
  return this.GetValue(false);
}
[SecuritySafeCritical]
private object GetValue(bool raw)
{
  object obj = MdConstant.GetValue(this.GetRuntimeModule().MetadataImport, this.m_tkField, this.FieldType.GetTypeHandleInternal(), raw);
  if (obj == DBNull.Value)
    throw new NotSupportedException(Environment.GetResourceString("Arg_EnumLitValueNotFound"));
  else
    return obj;
}

When you get a FieldInfo for the non-constant value you get the RtFieldInfo type and it does do a security check.

Here's decompiled code for the RtFieldInfo type:

public override object GetValue(object obj)
{
  StackCrawlMark stackMark = StackCrawlMark.LookForMyCaller;
  return this.InternalGetValue(obj, ref stackMark);
}

[SecuritySafeCritical]
[DebuggerStepThrough]
[DebuggerHidden]
internal object InternalGetValue(object obj, ref StackCrawlMark stackMark)
{
  INVOCATION_FLAGS invocationFlags = this.InvocationFlags;
  RuntimeType runtimeType1 = this.DeclaringType as RuntimeType;
  if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_NO_INVOKE) != INVOCATION_FLAGS.INVOCATION_FLAGS_UNKNOWN)
  {
    if (runtimeType1 != (RuntimeType) null && this.DeclaringType.ContainsGenericParameters)
      throw new InvalidOperationException(Environment.GetResourceString("Arg_UnboundGenField"));
    if (runtimeType1 == (RuntimeType) null && this.Module.Assembly.ReflectionOnly || runtimeType1 is ReflectionOnlyType)
      throw new InvalidOperationException(Environment.GetResourceString("Arg_ReflectionOnlyField"));
    else
      throw new FieldAccessException();
  }
  else
  {
    this.CheckConsistency(obj);
    if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_NON_W8P_FX_API) != INVOCATION_FLAGS.INVOCATION_FLAGS_UNKNOWN)
    {
      RuntimeAssembly executingAssembly = RuntimeAssembly.GetExecutingAssembly(ref stackMark);
      if ((Assembly) executingAssembly != (Assembly) null && !executingAssembly.IsSafeForReflection())
        throw new InvalidOperationException(Environment.GetResourceString("InvalidOperation_APIInvalidForCurrentContext", new object[1]
        {
          (object) this.FullName
        }));
    }
    RuntimeType runtimeType2 = (RuntimeType) this.FieldType;
    if ((invocationFlags & INVOCATION_FLAGS.INVOCATION_FLAGS_NEED_SECURITY) != INVOCATION_FLAGS.INVOCATION_FLAGS_UNKNOWN)
      RtFieldInfo.PerformVisibilityCheckOnField(this.m_fieldHandle, obj, this.m_declaringType, this.m_fieldAttributes, (uint) (this.m_invocationFlags & ~INVOCATION_FLAGS.INVOCATION_FLAGS_IS_CTOR));
    return this.UnsafeGetValue(obj);
  }
}
Vyacheslav Volkov
  • 4,592
  • 1
  • 20
  • 20
  • Thanks *Vyacheslav* for the decompiled code that explicitly shows that security check is not implemented for literal fields. The main question remains: why? +1 anyway :) – Pragmateek Sep 27 '13 at 13:26