在沙盒 Appdomain 中加载程序集 - SecurityException

Posted

技术标签:

【中文标题】在沙盒 Appdomain 中加载程序集 - SecurityException【英文标题】:Loading Assembly in sandbox Appdomain - SecurityException 【发布时间】:2014-09-30 19:48:09 【问题描述】:

我想从运行时创建的程序集中调用方法。它是部分受信任的代码,所以我想为它创建一个沙盒应用程序域。

我使用 Roslyn 创建程序集,结果是 byte[]。我可以在默认的Appdomain 中加载并从中调用它,它工作正常。问题是(我猜)沙盒。

我用this tutorial。

创建沙盒Appdomain

private AppDomain createAppdomain(string location)

     AppDomain currentAppdomain = AppDomain.CurrentDomain;

     // Create the permission set to be granted to the untrusted application
     Evidence ev = new Evidence();
     ev.AddHostEvidence(new Zone(SecurityZone.Internet));
     PermissionSet internetPS = SecurityManager.GetStandardSandbox(ev);
     var platform = Assembly.GetExecutingAssembly();
     internetPS.AddPermission(new FileIOPermission(FileIOPermissionAccess.Read | FileIOPermissionAccess.PathDiscovery, Path.GetDirectoryName(platform.Location)));

     // Sign the assembly that contains the hosting class (named Sandboxer in this example) that calls the untrusted code
     // .NET Framework assemblies such as mscorlib and System.dll do not have to be added to the full-trust list
     // because they are loaded as fully trusted from the global assembly cache.
     StrongName[] fullTrustAssembly = new StrongName[1];
     fullTrustAssembly[0] = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<StrongName>();

     // Initialize the AppDomainSetup parameter of the CreateDomain method
     //  The ApplicationBase property is an important setting,
     // and should be different from the ApplicationBase property for the AppDomain of the hosting application.
     // If the ApplicationBase settings are the same,
     // the partial-trust application can get the hosting application to load (as fully trusted) an exception it defines, thus exploiting it.
     AppDomainSetup adSetup = new AppDomainSetup();
     adSetup.ApplicationBase = Path.GetFullPath(location);


     // Call the CreateDomain(String, Evidence, AppDomainSetup, PermissionSet, StrongName[]) method overload to create the application domain
     // http://msdn.microsoft.com/en-us/library/ms130766(v=vs.110).aspx
     AppDomain newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, internetPS, fullTrustAssembly);

     return newDomain;

创建沙盒(沙盒类型为MarshalByRefObject):

string physicalPath = HttpContext.Current.Request.PhysicalApplicationPath + @"App_Data\";
 AppDomain Sandbox = createAppdomain(physicalPath);

// http://msdn.microsoft.com/en-us/library/dd413384(v=vs.110).aspx
ObjectHandle handle = Activator.CreateInstanceFrom(
     Sandbox,
     typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
     typeof(Sandboxer).FullName,
     true,
     BindingFlags.Public | BindingFlags.Instance | BindingFlags.CreateInstance,
     null,
    // byte[] rawAssembly
     new object[]  rawAssembly ,
     null,
     null);
Sandboxer newDomainInstance = (Sandboxer)handle.Unwrap();   

string s = newDomainInstance.callMethod();

加载Assembly,调用方法:

private string callMethod()
   
     // No Exception thrown yet, but some problems with the Evidence:
     // Evidence    'asm.Evidence' threw an exception of type 'System.Security.SecurityException'   System.Security.Policy.Evidence System.Security.SecurityException
     //"Request for the permission of type 'System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed."
     Assembly asm = Assembly.Load(rawAssembly);

     Type MyClass = asm.GetType(myClassName);

     // In this line I get the Exception:
     // System.Security.SecurityException
     // "Request failed."
     object obj = Activator.CreateInstance(MyClass);

     MethodInfo mi = MyClass.GetMethod(myMethodName);
     mi.Invoke(obj, null);

     // some code

     return s;

堆栈跟踪:

"at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)\
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)\r\n   at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type) ..."

我错过了什么? (对不起我的英语。)

编辑: 尝试将此行添加到类以授予完全权限:

[PermissionSet(SecurityAction.Assert, Unrestricted = true)]

在此之后object obj = Activator.CreateInstance(MyClass); 工作正常。我需要沙盒,所以这不是解决方案。

【问题讨论】:

您可能希望从不受限制/无处不在的权限开始,以查看代码是否正常工作,而不是开始一一删除以查看哪个失败。 由于某种原因我(还)无法解决,如果您使用 Assembly.Load(byte[]) 加载程序集,您的沙箱程序集将尝试要求完全信任而不是获得它。即使它在完全信任的程序集列表中。如果您改为使用 Assembly.LoadFrom(string),它会按预期工作......去看看! 想通了,即使我不完全相信,我可能会自己发布一个问题。请参阅下面的答案! 【参考方案1】:

我遇到了同样的问题,并且撞了一个小时。 因为我在另一个示例中运行良好的相同代码! 所以我开始删除东西,直到我发现唯一的区别在于程序集的加载方式:Assembly.LoadFrom 工作正常。 Assembly.Load(byte[]) 总是给我一个安全例外。

异常表明我的沙盒 DLL 是问题的根源,但没有帮助。因为它在 MSDN 文档中被证明是一句话:

使用此方法加载的程序集的信任级别与调用程序集的信任级别相同。要从具有应用程序域信任级别的字节数组加载程序集,请使用 Load(Byte[], Byte[], SecurityContextSource) 方法重载

哎呀!我想在权限非常有限的 appdomain 中加载具有“完全信任”的程序集,而不是在完全受信任的列表中,使 CLR 不高兴。从加载的程序集中执行的第一行 -> bang! (这就是我断言异常令人困惑的原因:它提到了“错误”的 DLL 作为异常源)

所以我用Load(Byte[], null, SecurityContextSource.CurrentAppDomain) 替换了Load(byte[]),现在它就像LoadFrom 一样工作

【讨论】:

【参考方案2】:

您的问题是您加载的 Sandboxer 程序集,而完全受信任的是使用不受信任的堆栈调用。因此,您在激活的某个地方遇到了安全异常(不是 100% 根据您的代码确定原因)。解决方案与您找到的完全一样,将断言添加到函数中。这将允许此函数以完全信任的方式执行(断言将阻止堆栈遍历),但加载的程序集是部分信任的,因此仍将被沙盒化。

如果您这样做,您需要确保沙盒代码无法与该方法交互,因此可能会滥用它。您可以通过标记函数 SecurityCritical 来做到这一点,这样只有完全受信任的代码才能与函数交互。

【讨论】:

我不明白为什么:如果 Sandboxer 完全受信任,它应该可以工作。另外,如果我使用 Assembly.LoadFrom 而不是 Assebly.Load(byte[]),它就可以工作。具有相同的权限集。这里还有更多事情发生......

以上是关于在沙盒 Appdomain 中加载程序集 - SecurityException的主要内容,如果未能解决你的问题,请参考以下文章

沙盒 AppDomain 跨程序集异常处理

在 AppDomain 问题中加载具有依赖项的程序集

无法在 appDomain 中加载程序集

无法在新的 appDomain 中加载程序集

在新的 Appdomain 中加载程序集,需要完全信任父程序集

在不同的 AppDomain 中加载具有依赖项的程序集