背景:野田时间 https://nodatime.org包含许多
可序列化的结构。虽然我不喜欢二进制序列化,但我们
早在 1.x 时间线中就收到了许多支持它的请求。
我们通过实施ISerializable
界面。
我们最近收到了一份问题
报告 https://github.com/nodatime/nodatime/issues/1060野田的
时间2.x.NET 中失败
小提琴 https://dotnetfiddle.net/vTCzMJ。使用 Noda 的相同代码
时间 1.x 工作正常。抛出的异常是这样的:
重写成员时违反了继承安全规则:
'NodaTime.Duration.System.Runtime.Serialization.ISerialized.GetObjectData(System.Runtime.Serialization.SerializationInfo,
System.Runtime.Serialization.StreamingContext)'。安全
重写方法的可访问性必须与安全性相匹配
被重写方法的可访问性。
我已将范围缩小到目标框架:1.x
目标 .NET 3.5(客户端配置文件); 2.x 针对 .NET 4.5。他们有
PCL 与 .NET Core 在支持方面存在巨大差异
项目文件结构,但看起来这无关紧要。
我已经成功在本地项目中重现了这个,但我还没有
找到了解决办法。
在VS2017中重现的步骤:
- 创建新的解决方案
- 创建面向 .NET 的新经典 Windows 控制台应用程序
4.5.1.我称之为“CodeRunner”。
- 在项目属性中,转到“签名”并使用以下命令对程序集进行签名
一把新钥匙。取消选中密码要求,并使用任意密钥文件名。
- 粘贴以下代码进行替换
Program.cs
。这是一
代码的缩写版本这个微软
样本 https://learn.microsoft.com/en-us/dotnet/framework/misc/how-to-run-partially-trusted-code-in-a-sandbox#example。
我保持所有路径相同,所以如果你想返回到
更完整的代码,您不需要更改任何其他内容。
Code:
using System;
using System.Security;
using System.Security.Permissions;
class Sandboxer : MarshalByRefObject
{
static void Main()
{
var adSetup = new AppDomainSetup();
adSetup.ApplicationBase = System.IO.Path.GetFullPath(@"..\..\..\UntrustedCode\bin\Debug");
var permSet = new PermissionSet(PermissionState.None);
permSet.AddPermission(new SecurityPermission(SecurityPermissionFlag.Execution));
var fullTrustAssembly = typeof(Sandboxer).Assembly.Evidence.GetHostEvidence<System.Security.Policy.StrongName>();
var newDomain = AppDomain.CreateDomain("Sandbox", null, adSetup, permSet, fullTrustAssembly);
var handle = Activator.CreateInstanceFrom(
newDomain, typeof(Sandboxer).Assembly.ManifestModule.FullyQualifiedName,
typeof(Sandboxer).FullName
);
Sandboxer newDomainInstance = (Sandboxer) handle.Unwrap();
newDomainInstance.ExecuteUntrustedCode("UntrustedCode", "UntrustedCode.UntrustedClass", "IsFibonacci", new object[] { 45 });
}
public void ExecuteUntrustedCode(string assemblyName, string typeName, string entryPoint, Object[] parameters)
{
var target = System.Reflection.Assembly.Load(assemblyName).GetType(typeName).GetMethod(entryPoint);
target.Invoke(null, parameters);
}
}
- 创建另一个名为“UntrustedCode”的项目。这应该是一个
经典桌面类库项目。
- 签署大会;您可以使用新密钥或与之前相同的密钥
代码运行者。 (这部分是为了模仿野田时间的情况,
部分是为了让代码分析愉快。)
- 将以下代码粘贴到
Class1.cs
(覆盖那里的内容):
Code:
using System;
using System.Runtime.Serialization;
using System.Security;
using System.Security.Permissions;
// [assembly: AllowPartiallyTrustedCallers]
namespace UntrustedCode
{
public class UntrustedClass
{
// Method named oddly (given the content) in order to allow MSDN
// sample to run unchanged.
public static bool IsFibonacci(int number)
{
Console.WriteLine(new CustomStruct());
return true;
}
}
[Serializable]
public struct CustomStruct : ISerializable
{
private CustomStruct(SerializationInfo info, StreamingContext context) { }
//[SecuritySafeCritical]
//[SecurityCritical]
//[SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
{
throw new NotImplementedException();
}
}
}
运行 CodeRunner 项目会出现以下异常(为了可读性而重新格式化):
未处理的异常:System.Reflection.TargetInitationException:
调用目标已引发异常。
--->
系统类型加载异常:
重写成员时违反了继承安全规则:
'UntrustedCode.CustomStruct.System.Runtime.Serialization.ISerialized.GetObjectData(...)。
重写方法的安全可访问性必须与安全性相匹配
被重写方法的可访问性。
注释掉的属性显示了我尝试过的内容:
-
SecurityPermission
由两篇不同的 MS 文章推荐(first https://learn.microsoft.com/en-us/dotnet/standard/serialization/serialization-guidelines,
second https://learn.microsoft.com/en-us/dotnet/standard/serialization/custom-serialization), 虽然
有趣的是,他们围绕显式/隐式接口实现做了不同的事情
-
SecurityCritical
是野田时间目前拥有的,也是这个问题的答案 https://stackoverflow.com/questions/3055792建议
-
SecuritySafeCritical
代码分析规则消息在某种程度上建议了
- Without any属性,代码分析规则都满意 - 两者之一
SecurityPermission
or SecurityCritical
目前,规则告诉您删除这些属性 - 除非您do have AllowPartiallyTrustedCallers
。在任何一种情况下遵循建议都没有帮助。
- 野田时间有
AllowPartiallyTrustedCallers
应用于它;无论是否应用该属性,此处的示例都不起作用。
如果我添加,代码运行不会出现异常[assembly: SecurityRules(SecurityRuleSet.Level1)]
to the UntrustedCode
程序集(并取消注释AllowPartiallyTrustedCallers
属性),但我认为这对于可能妨碍其他代码的问题来说是一个糟糕的解决方案。
我完全承认,当谈到这种事情时,我感到非常迷失
.NET 的安全方面。所以呢can我确实以 .NET 4.5 为目标
但允许我的类型实现ISerializable
并仍被用于
.NET Fiddle 等环境?
(虽然我的目标是 .NET 4.5,但我相信是 .NET 4.0 安全策略更改导致了问题,因此有了标签。)