在沙箱AppDomain中执行反射方法时的SecurityException

时间:2016-10-27 12:47:09

标签: c# reflection appdomain

我使用单独的AppDomain作为沙箱,并尝试执行通过反射构建的方法。

调用方法时,

  

SecurityException

即使沙箱AppDomain在其ReflectionPermission(PermissionState.Unrestricted)上设置PermissionSet,也会抛出

....

PermissionSet设置为PermissionState.Unrestricted时,调用会起作用,但这会破坏沙箱的用途。

以下是一个演示此问题的示例:

using System;
using System.Security;
using System.Security.Permissions;

namespace ConsoleTest
{
    class Program
    {
        static void Main(string[] args)
        {
            var person = new Person();
            var program = new Program();

            var customDomain = program.CreateDomain();
            var result = program.Execute(customDomain, (x) =>
            {
                var type = x.GetType();
                var propertyInfo = type.GetProperty("Name");
                var method = propertyInfo.GetMethod;
                var res = method.Invoke(x, null) as string;
                return res;
            }, person);
            Console.WriteLine(result);
            Console.ReadLine();
        }

        public object Execute(AppDomain domain, Func<object, object> toExecute, params object[] parameters)
        {
            var proxy = new Proxy(toExecute, parameters);
            var result = proxy.Invoke(domain);
            return result;
        }

        private AppDomain CreateDomain()
        {
            var appDomainSetup = new AppDomainSetup()
            {
                ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
                ApplicationName = "UntrustedAppDomain"
            };

            // Set up permissions
            var permissionSet = new PermissionSet(PermissionState.None);
            permissionSet.AddPermission(new SecurityPermission(PermissionState.Unrestricted));
            permissionSet.AddPermission(new ReflectionPermission(PermissionState.Unrestricted));

            // Create the app domain.
            return AppDomain.CreateDomain("UntrustedAppDomain", null, appDomainSetup, permissionSet);
        }

        private sealed class Proxy : MarshalByRefObject
        {
            private Delegate method;
            private object[] args;
            private object result;

            public Proxy(Delegate method, params object[] parameters)
            {
                this.method = method;
                this.args = parameters;
            }

            public object Invoke(AppDomain customDomain)
            {
                customDomain.DoCallBack(Execute);
                return this.result;
            }

            private void Execute()
            {
                this.result = this.method.DynamicInvoke(this.args);
            }
        }
    }

    public class Person
    {
        public Person()
        {
            this.Name = "Test Person";
        }

        public string Name { get; set; }
    }
}

1 个答案:

答案 0 :(得分:2)

这是我上面评论的补充

  

在沙箱AppDomain中执行反射方法时出现SecurityException

您正尝试从辅助域中的上下文在主AppDomain 中创建的对象上执行方法。换句话说,沙箱中的代码试图在主域中调用不允许的代码。通过AppDomains使用沙箱时,必须通过{strong>主应用领域中的CreateInstanceAndUnwrap在沙箱域中创建

改变这个:

Proxy

...为:

public object Execute(AppDomain domain, Func<object, object> toExecute, params object[] parameters)
    {
        var proxy = new Proxy(toExecute, parameters);
        var result = proxy.Invoke(domain);
        return result;
    }

...并 public object Execute(AppDomain domain, Func<object, object> toExecute, params object[] parameters) { var t = typeof(Proxy); // add me var args = new object[] {toExecute, parameters}; var proxy = domain.CreateInstanceAndUnwrap(t.Assembly.FullName, t.FullName, false, BindingFlags.Default, null, args, null, null) as Proxy; // add me //var proxy = new Proxy(toExecute, parameters); var result = proxy.Invoke(domain); return result; } Person[Serializable]派生,具体取决于您是否要将副本传输到传递间接可修改分别对着沙箱的对象。

  

感谢您的回复。如果在沙箱域中创建了Proxy,并且Person类是Serializable,则上面的示例代码可以工作,但是在辅助域中使用的person对象实际上与主域中的person对象不同 - 它被序列化在CreateInstanceAndUnwrap方法

这是正确的,是设计的。请注意,您帖子中显示的MarshalByRefObject未标记为Person,因此会导致错误。我假设你修好了。如果您希望传递“相同”对象,则该类派生自Serializable

以下是完整的代码:

MarshalByRefObject

...产生以下输出:

enter image description here

安全风险

您的设计是否需要沙箱中的代码来修改其他应用程序域中的对象。在我看来,这是一个安全风险,可以说否定了沙箱的目的。因此,如果您必须传递域间对象,请将它们作为using System; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Security; using System.Security.Permissions; using System.Text; using System.Threading.Tasks; namespace Sandboxes1 { class Program { static void Main(string[] args) { var person = new Person(); Console.WriteLine("[{0}] Person's current name: {1}", AppDomain.CurrentDomain.FriendlyName, person.Name); var program = new Program(); var customDomain = program.CreateDomain(); var result = program.Execute(customDomain, (x) => { Console.WriteLine("[{0}] Inside delegate", AppDomain.CurrentDomain.FriendlyName); var type = x.GetType(); var propertyInfo = type.GetProperty("Name"); var method = propertyInfo.GetMethod; var res = method.Invoke(x, null) as string; dynamic d = x; d.Name = "Fozzy Bear"; Console.WriteLine("[{0}] delegate changed person's name to- {1}", AppDomain.CurrentDomain.FriendlyName, d.Name); return res; }, person); Console.WriteLine("[{0}] Result: {1}", AppDomain.CurrentDomain.FriendlyName, result); Console.WriteLine("[{0}] Person's current name: {1}", AppDomain.CurrentDomain.FriendlyName, person.Name); Console.ReadLine(); } public object Execute(AppDomain domain, Func<object, object> toExecute, params object[] parameters) { var t = typeof(Proxy); // add me var args = new object[] {toExecute, parameters}; var proxy = domain.CreateInstanceAndUnwrap(t.Assembly.FullName, t.FullName, false, BindingFlags.Default, null, args, null, null) as Proxy; // add me //var proxy = new Proxy(toExecute, parameters); var result = proxy.Invoke(domain); return result; } private AppDomain CreateDomain() { var appDomainSetup = new AppDomainSetup() { ApplicationBase = AppDomain.CurrentDomain.BaseDirectory, ApplicationName = "UntrustedAppDomain" }; // Set up permissions var permissionSet = new PermissionSet(PermissionState.None); permissionSet.AddPermission(new SecurityPermission(PermissionState.Unrestricted)); permissionSet.AddPermission(new ReflectionPermission(PermissionState.Unrestricted)); // Create the app domain. return AppDomain.CreateDomain("UntrustedAppDomain", null, appDomainSetup, permissionSet); } private sealed class Proxy : MarshalByRefObject { private Delegate method; private object[] args; private object result; public Proxy(Delegate method, params object[] parameters) { Console.WriteLine("[{0}] Proxy()", AppDomain.CurrentDomain.FriendlyName); this.method = method; this.args = parameters; } public object Invoke(AppDomain customDomain) { Console.WriteLine("[{0}] Invoke()", AppDomain.CurrentDomain.FriendlyName); customDomain.DoCallBack(Execute); return this.result; } private void Execute() { Console.WriteLine("[{0}] Execute()", AppDomain.CurrentDomain.FriendlyName); this.result = this.method.DynamicInvoke(this.args); } } } [Serializable] public class Person { private string _name; public Person() { Name = "Test Person"; } public string Name { get { Console.WriteLine("[{0}] Person.getName()", AppDomain.CurrentDomain.FriendlyName); return _name; } set { _name = value; } } } } 传递给对象的副本。

更好的安全性?

我倾向于在代码中使用的方法是:

  1. 定义一个Serializable类,用于创建辅助Manager
  2. AppDomain使用Manager在辅助CreateInstanceAndUnwrap
  3. 中创建MarshalByRefObject Proxy课程
  4. AppDomain调用Manager来说Proxy。这意味着任何插件创建的对象现在都自动存在于辅助LoadPlugins()
  5. 确保从AppDomain中包含从主AppDomain调用的Proxy方法的任何方法。 不要让任何异常冒泡到主AppDomain ,因为来自可疑插件的狡猾代码很可能会抛出一个自定义异常,当最终确定时可以执行任何希望的代码完全权限由于调用堆栈现在位于主AppDomain上。这是.NET编程中为数不多的几个场景之一,您必须try-catch所有内容而不是catch
  6. 如果主AppDomain需要与辅助应用程序域中的对象进行通信,请通过调用throw并通过代表您调用该方法来执行此操作。再次确保此代理方法完全包含在Proxy所有
  7. 结论

    我不知道try-catch,现在我看到它我不喜欢它。委托中意外使用对象的风险太大,特别是在一个方法中定义委托代码的情况下,该方法将是由一个域执行的某些代码的混合;其他部分由另一个域。

    更多信息

    • 在大约2006年的AppDomains上有一篇精彩的MSDN杂志文章“加载项 - 你信任它”(或其他一些)(或其他一些),我从中得到了答案。可悲的是,微软已经将HTML表格脱机,只有很难搜索的.chm文件。