我正在尝试使用app-domain构建沙箱,以隔离可能不良代码的执行。
除其他事项外,我还要限制反思。
我正在用这种方式构建沙箱:
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);
它与私有实例字段和私有属性一起正常工作:运行时拒绝任何在沙箱中访问它们的尝试。
但是我注意到它不适用于文字字段(C#中的 const ):它始终可以获得文字的值字段,即使是私有的:
private const string PASSWORD = "secret";
private string password = "secret";
private string Password
{
get
{
return "secret";
}
}
密码和密码受到正确保护,但任何代码都可以通过基本反射获得 PASSWORD 的值:
string password = typeof(User).GetField("PASSWORD", BindingFlags.NonPublic | BindingFlags.Static).GetValue(currentUser) as string; // OK no problem take it, it's free!
我想理解这种行为背后的基本原理:是因为文字值在程序集中总是“容易”可见,因此防止反射是一场失败的战斗,或者因为最终值并未真正“被调用”所以没有安全检查或......?
这个例子并不真正相关,因为密码不会被共享,但想象一下秘密值是用于加密的盐值还是类似的......
感谢您的帮助。
答案 0 :(得分:2)
当您获得常量FieldInfo
时,您会获得MdFieldInfo
类型。
如果我们反编译代码,我们将看到它没有对该类型进行安全检查。
以下是MdFieldInfo
类型的反编译代码:
[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;
}
如果您获得非常量值的FieldInfo
,则会获得RtFieldInfo
类型,并且会进行安全检查。
以下是RtFieldInfo
类型的反编译代码:
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);
}
}