如何在 .NET 4+ 中实现 ISerialized 而不违反继承安全规则?

2024-04-29

背景:野田时间 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 安全策略更改导致了问题,因此有了标签。)


根据the MSDN https://learn.microsoft.com/en-us/dotnet/standard/serialization/custom-serialization,在.NET 4.0中基本上你不应该使用ISerializable对于部分受信任的代码,您应该使用ISafe序列化数据 https://learn.microsoft.com/en-us/dotnet/api/system.runtime.serialization.isafeserializationdata?view=netframework-4.7.1

引用自https://learn.microsoft.com/en-us/dotnet/standard/serialization/custom-serialization https://learn.microsoft.com/en-us/dotnet/standard/serialization/custom-serialization

重要的

在 .NET Framework 4.0 之前的版本中,部分受信任的程序集中的自定义用户数据的序列化是使用 GetObjectData 完成的。从版本 4.0 开始,该方法使用 SecurityCriticalAttribute 属性进行标记,该属性可防止在部分受信任的程序集中执行。要解决此问题,请实现 ISafeSerializationData 接口。

所以如果你需要的话,可能不是你想听到的,但我认为在继续使用的同时没有任何办法可以解决它ISerializable(除了返回Level1安全,你说你不想)。

PS: the ISafeSerializationData文档声明它只是针对例外情况,但它似乎并不那么具体,您可能想尝试一下...我基本上无法用您的示例代码测试它(除了删除ISerializable有效,但你已经知道了)...你必须看看是否ISafeSerializationData足够适合你。

PS2:SecurityCritical属性不起作用,因为当程序集以部分信任模式加载时它会被忽略(2 级安全)。如果您调试,您可以在示例代码中看到它target变量在ExecuteUntrustedCode在调用它之前,它会IsSecurityTransparent to true and IsSecurityCritical to false即使您用SecurityCritical属性)

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 .NET 4+ 中实现 ISerialized 而不违反继承安全规则? 的相关文章

随机推荐