我使用单独的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; }
}
}
答案 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
...产生以下输出:
您的设计是否需要沙箱中的代码来修改其他应用程序域中的对象。在我看来,这是一个安全风险,可以说否定了沙箱的目的。因此,如果您必须传递域间对象,请将它们作为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; }
}
}
}
传递给对象的副本。
我倾向于在代码中使用的方法是:
Serializable
类,用于创建辅助Manager
AppDomain
使用Manager
在辅助CreateInstanceAndUnwrap
MarshalByRefObject
Proxy
课程
AppDomain
调用Manager
来说Proxy
。这意味着任何插件创建的对象现在都自动存在于辅助LoadPlugins()
AppDomain
中包含从主AppDomain调用的Proxy
方法的任何方法。 不要让任何异常冒泡到主AppDomain ,因为来自可疑插件的狡猾代码很可能会抛出一个自定义异常,当最终确定时可以执行任何希望的代码完全权限由于调用堆栈现在位于主AppDomain上。这是.NET编程中为数不多的几个场景之一,您必须try-catch
所有内容而不是catch
throw
并通过代表您调用该方法来执行此操作。再次确保此代理方法完全包含在Proxy
所有我不知道try-catch
,现在我看到它我不喜欢它。委托中意外使用对象的风险太大,特别是在一个方法中定义委托代码的情况下,该方法将是由一个域执行的某些代码的混合;其他部分由另一个域。