订阅域事件时的 C# AppDomain 沙箱安全异常

标签 c# security exception appdomain sandbox

我正在编写一个插件系统来在我的服务器应用程序(C#、.NET 4.0)中运行客户端提供的不受信任的代码。为此,我在一个新的沙盒 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 提供完整反射能力的情况下解决它.请问有人有什么建议或解释吗?

最佳答案

简答:

用于将处理程序添加到事件 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 模式下执行它。故事结束。

长答案:

您正在处理跨域通信。意味着您的事件处理程序将在沙盒域的空间中执行,然后使用远程处理注册到您的父域。您提供的代码需要反射权限,无论您在该方法中使用什么事件都无关紧要 - 安全关键与否。

因此,如果您希望您的沙盒域与您的父域进行安全通信,您应该使用经典的 .NET 远程处理方法,此代码将不需要任何额外的权限,并允许将沙盒域上发生的事件通知父域:

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();
        }


    }
}

关于订阅域事件时的 C# AppDomain 沙箱安全异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/7784615/

相关文章:

java - 大括号阻止编译

java - Java SDK for REST API服务上的错误处理

c# - 以管理员身份运行多个应用程序

c# - 调整从列表中选择项目的机会

security - 端到端加密和同步

javascript - 防止 CKEditor 中的 XSS

c# - 在网络上发布站点

c# - 从 .net core 6.0 连接到在 Docker 中运行的 Sql Edge Server

php - USI 网络服务 SOAP 格式

javascript - Node 不抛出堆栈溢出异常