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

发布于 2024-12-09 20:15:36 字数 4933 浏览 0 评论 0原文

我正在编写一个插件系统来在我的服务器应用程序(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 )
        {

        }
    }
}

运行此代码后,我在域上遇到以下异常。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 提供完整反射能力的情况下解决它。请问有人有建议或解释吗?

I'm writing a plugin system to run client-provided untrusted code in my server application (C#, .NET 4.0). In order to do this, i'm running each plugin in a new sandboxed AppDomain.

However, I'm stuck on a security exception that I don't really understand the reason for. I have made a streamlined console application sample to illustrate the problem:

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 )
        {

        }
    }
}

Upon running this code, I'm getting the following exception on the domain.AssemblyLoad line:

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>

My best guess is that there's some event-subscription code under the hood executing in the new sandboxed AppDomain without the required security permissions, but I don't know how to work around it without giving full reflection-capacity to the sandboxed AppDomain. Does anyone have a suggestion or explanation, please?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

追星践月 2024-12-16 20:15:36

简短回答

用于向事件 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 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();
        }


    }
}

Short answer:

Hidden method for adding handler to event AppDomain.AssemblyLoad - marked by SecurityCriticalAttribute. Check 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

In order to execute this method you MUST execute it in FullTrust mode (of your sandbox domain). End of story.

Long answer:

You are dealing with Cross Domain communication. Means your event handler will be executed in the space of Sandbox domain, and then using remoting enrolled to you parent domain. The code you provided requires Reflection permission, it does not matter what event you use in that approach - security critical or not.

So if you want your sandbox domain to do safe communication with your parent domain you should use classic .NET Remoting approach, this code will not require any additional permissions and allow to notify parent domain about events happened on sandbox domain:

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


    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文