2011-10-16 74 views
7

我正在編寫一個插件系統來在我的服務器應用程序(C#,.NET 4.0)中運行客戶端提供的不可信代碼。爲了做到這一點,我在新的沙箱AppDomain中運行每個插件。訂閱域事件時出現C#AppDomain沙箱安全異常

但是,我陷入了一個安全異常,我不明白原因。我做了一個精簡的控制檯應用程序示例來說明這個問題:

namespace SandboxTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Sandbox sandbox = new Sandbox(); 
      Console.ReadLine(); 
     } 
    } 

    class Sandbox 
    { 
     AppDomain domain; 

     public Sandbox() 
     { 
      PermissionSet ps = new PermissionSet(PermissionState.None); 
      ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution)); 

      try 
      { 
       domain = AppDomain.CreateDomain("Sandbox", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation, ps); 
       domain.AssemblyLoad += new AssemblyLoadEventHandler(domain_AssemblyLoad); 
       domain.AssemblyResolve += new ResolveEventHandler(domain_AssemblyResolve); 
      } 
      catch(Exception e) 
      { 
       Trace.WriteLine(e.ToString()); 
       throw e; 
      } 
     } 

     static Assembly domain_AssemblyResolve(object sender, ResolveEventArgs args) 
     { 
      return null; 
     } 

     static void domain_AssemblyLoad(object sender, AssemblyLoadEventArgs args) 
     { 

     } 
    } 
} 

在運行此代碼,我得到的domain.AssemblyLoad線以下異常:

A first chance exception of type 'System.Security.SecurityException' occurred in SandboxTest.exe 
'SandboxTest.vshost.exe' (Managed (v4.0.30319)): Loaded 'C:\Windows\Microsoft.Net\assembly\GAC_MSIL\System.Configuration\v4.0_4.0.0.0__b03f5f7f11d50a3a\System.Configuration.dll', Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled. 
System.Security.SecurityException: Request for the permission of type 'System.Security.Permissions.ReflectionPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' failed. 
    at System.Security.CodeAccessSecurityEngine.ThrowSecurityException(RuntimeAssembly asm, PermissionSet granted, PermissionSet refused, RuntimeMethodHandleInternal rmh, SecurityAction action, Object demand, IPermission permThatFailed) 
    at System.Security.CodeAccessSecurityEngine.ThrowSecurityException(Object assemblyOrString, PermissionSet granted, PermissionSet refused, RuntimeMethodHandleInternal rmh, SecurityAction action, Object demand, IPermission permThatFailed) 
    at System.Security.CodeAccessSecurityEngine.CheckHelper(PermissionSet grantedSet, PermissionSet refusedSet, CodeAccessPermission demand, PermissionToken permToken, RuntimeMethodHandleInternal rmh, Object assemblyOrString, SecurityAction action, Boolean throwException) 
    at System.Security.CodeAccessSecurityEngine.CheckHelper(CompressedStack cs, PermissionSet grantedSet, PermissionSet refusedSet, CodeAccessPermission demand, PermissionToken permToken, RuntimeMethodHandleInternal rmh, RuntimeAssembly asm, SecurityAction action) 
    at System.Security.CodeAccessSecurityEngine.Check(Object demand, StackCrawlMark& stackMark, Boolean isPermSet) 
    at System.Security.CodeAccessSecurityEngine.Check(CodeAccessPermission cap, StackCrawlMark& stackMark) 
    at System.Security.CodeAccessPermission.Demand() 
    at System.DelegateSerializationHolder.GetDelegateSerializationInfo(SerializationInfo info, Type delegateType, Object target, MethodInfo method, Int32 targetIndex) 
    at System.MulticastDelegate.GetObjectData(SerializationInfo info, StreamingContext context) 
    at System.Runtime.Serialization.ObjectCloneHelper.GetObjectData(Object serObj, String& typeName, String& assemName, String[]& fieldNames, Object[]& fieldValues) 


    at System.AppDomain.add_AssemblyLoad(AssemblyLoadEventHandler value) 
    at SandboxTest.Sandbox..ctor() in C:\Dev\Projects\Botfield\SandboxTest\Program.cs:line 36 
The action that failed was: 
Demand 
The type of the first permission that failed was: 
System.Security.Permissions.ReflectionPermission 
The first permission that failed was: 
<IPermission class="System.Security.Permissions.ReflectionPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
version="1" 
Flags="MemberAccess"/> 

The demand was for: 
<IPermission class="System.Security.Permissions.ReflectionPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
version="1" 
Flags="MemberAccess"/> 

The granted set of the failing assembly was: 
<PermissionSet class="System.Security.PermissionSet" 
version="1"> 
<IPermission class="System.Security.Permissions.SecurityPermission, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
version="1" 
Flags="Execution"/> 
</PermissionSet> 

我最好的猜測是有一些事件訂閱代碼在新的沙盒AppDomain中執行,但沒有必要的安全權限,但我不知道如何在沙盒AppDomain沒有完全反射容量的情況下解決它。有人有任何建議或解釋嗎?

回答

4

簡短的回答

添加處理程序事件AppDomain.AssemblyLoad隱藏的方法 - 通過性SecurityCriticalAttribute標記。檢查ILASM:

.method public hidebysig newslot specialname virtual final 
     instance void add_AssemblyLoad(class System.AssemblyLoadEventHandler 'value') cil managed 
{ 
    .custom instance void System.Security.SecurityCriticalAttribute::.ctor() = (01 00 00 00) 
    // Code size  0 (0x0) 
} // end of method AppDomain::add_AssemblyLoad 

爲了執行這個方法必須在FullTrust模式執行它(沙箱域)。故事結局。

龍回答

你正在處理的跨域通信。意味着您的事件處理程序將在Sandbox域的空間中執行,然後使用註冊到您的父域的遠程處理。您提供的代碼需要反射許可,在該方法中使用哪種事件並不重要 - 安全性至關重要。

因此,如果您希望您的沙箱域與您的父域進行安全通信,則應使用經典的.NET Remoting方法,此代碼將不需要任何其他權限並允許向沙發域上發生的事件通知父域:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Security; 
using System.Security.Permissions; 
using System.Diagnostics; 
using System.Reflection; 

namespace SandboxTest 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      Sandbox sandbox = new Sandbox(); 
      Console.ReadLine(); 
     } 
    } 

    class Sandbox 
    { 
     AppDomain domain; 

     public Sandbox() 
     { 
      PermissionSet ps = new PermissionSet(PermissionState.None); 
      ps.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution)); 

      try 
      { 
       domain = AppDomain.CreateDomain("Sandbox", AppDomain.CurrentDomain.Evidence, AppDomain.CurrentDomain.SetupInformation, ps); 
       var tp = typeof(MyInit); 

       var obj = (MyInit)domain.CreateInstanceAndUnwrap(tp.Assembly.FullName, tp.FullName); 
       var myCallBack = new MyCallBack(); 
       myCallBack.Generated += new EventHandler(myCallBack_Generated); 
       obj.Subscribe(myCallBack); 
       obj.GenerateCallBackEvent(); 
      } 
      catch (Exception e) 
      { 
       Trace.WriteLine(e.ToString()); 
       throw e; 
      } 
     } 

     void myCallBack_Generated(object sender, EventArgs e) 
     { 
      //Executed in parent domain 
     } 



    } 


    public class MyCallBack:MarshalByRefObject 
    { 
     public void GenerateEvent() 
     { 
      //Executed in parent domain, but triggered by sandbox domain 
      if (Generated != null) Generated(this, null); 
     } 

     //for parent domain only 
     public event EventHandler Generated; 
    } 

    public class MyInit:MarshalByRefObject 
    { 
     public MyInit() 
     { 

     } 
     MyCallBack callback; 
     public void Subscribe(MyCallBack callback) 
     { 
      //executed on sandbox domain 
      this.callback = callback; 
     } 

     public void GenerateCallBackEvent() 
     { 
      //executed on sandbox domain 
      callback.GenerateEvent(); 
     } 


    } 
}