如何在不违反继承安全规则的情况下在.NET 4+中实现ISerializable? [英] How can I implement ISerializable in .NET 4+ without violating inheritance security rules?

查看:106
本文介绍了如何在不违反继承安全规则的情况下在.NET 4+中实现ISerializable?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

背景: Noda时间包含许多 可序列化的结构.虽然我不喜欢二进制序列化,但我们 在1.x时间线中收到了许多支持它的请求. 我们通过实现ISerializable接口来支持它.

Background: Noda Time contains many serializable structs. While I dislike binary serialization, we received many requests to support it, back in the 1.x timeline. We support it by implementing the ISerializable interface.

我们收到了最近的问题 野田的报告 时间2.x 在.NET中失败 小提琴.使用Noda的相同代码 时间1.x可以正常工作.抛出的异常是这样的:

We've received a recent issue report of Noda Time 2.x failing within .NET Fiddle. The same code using Noda Time 1.x works fine. The exception thrown is this:

在重写成员时违反了继承安全性规则: 'NodaTime.Duration.System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)".安全 覆盖方法的可访问性必须与安全性相匹配 覆盖的方法的可访问性.

Inheritance security rules violated while overriding member: 'NodaTime.Duration.System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)'. Security accessibility of the overriding method must match the security accessibility of the method being overriden.

我已经将其缩小为目标框架:1.x 针对.NET 3.5(客户端配置文件); 2.x面向.NET 4.5.他们有 在支持PCL与.NET Core以及 项目文件的结构,但这似乎无关紧要.

I've narrowed this down to the framework that's targeted: 1.x targets .NET 3.5 (client profile); 2.x targets .NET 4.5. They have big differences in terms of support PCL vs .NET Core and the project file structure, but it looks like this is irrelevant.

我设法在本地项目中重现了这一点,但是我没有 找到了解决方案.

I've managed to reproduce this in a local project, but I haven't found a solution to it.

在VS2017中复制的步骤:

Steps to reproduce in VS2017:

  • 创建新的解决方案
  • 创建一个针对.NET的新的经典Windows控制台应用程序 4.5.1.我称它为"CodeRunner".
  • 在项目属性中,转到签名",然后使用 一个新的密钥.取消选中密码要求,然后使用任何密钥文件名.
  • 粘贴以下代码以替换Program.cs.这是一

代码:

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"的项目.这应该是 经典桌面类库项目.
  • 在大会上签名;您可以使用一个新密钥,也可以使用相同的密钥 CodeRunner. (这部分是为了模仿Noda Time的情况, 并使代码分析感到高兴.)
  • 将以下代码粘贴到Class1.cs中(覆盖其中的内容):
    • Create another project called "UntrustedCode". This should be a Classic Desktop Class Library project.
    • Sign the assembly; you can use a new key or the same one as for CodeRunner. (This is partially to mimic the Noda Time situation, and partly to keep Code Analysis happy.)
    • Paste the following code in Class1.cs (overwriting what's there):
    • 代码:

      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项目会产生以下异常(为便于阅读而进行了重新格式化):

      Running the CodeRunner project gives the following exception (reformatted for readability):

      未处理的异常:System.Reflection.TargetInvocationException:
      调用的目标已引发异常.
      --->
      System.TypeLoadException:
      重写成员时违反了继承安全性规则:
      'UntrustedCode.CustomStruct.System.Runtime.Serialization.ISerializable.GetObjectData(...).
      覆盖方法的安全性可访问性必须与安全性相匹配
      覆盖的方法的可访问性.

      Unhandled Exception: System.Reflection.TargetInvocationException:
      Exception has been thrown by the target of an invocation.
      --->
      System.TypeLoadException:
      Inheritance security rules violated while overriding member:
      'UntrustedCode.CustomStruct.System.Runtime.Serialization.ISerializable.GetObjectData(...).
      Security accessibility of the overriding method must match the security
      accessibility of the method being overriden.

      已注释掉的属性显示了我尝试过的事情:

      The commented-out attributes show things I've tried:

      • SecurityPermission由两篇不同的MS文章推荐( 第二),尽管 有趣的是,他们围绕显式/隐式接口实现做了不同的事情
      • SecurityCritical是Noda Time当前拥有的内容,也是此问题的答案所建议的
      • 代码分析规则消息建议使用
      • SecuritySafeCritical
      • 没有 any 属性,代码分析规则很满意-使用SecurityPermissionSecurityCritical 目前,规则会告诉您删除属性-除非您要做具有AllowPartiallyTrustedCallers.无论哪种情况,遵循建议都无济于事.
      • Noda Time已应用AllowPartiallyTrustedCallers;此处的示例在应用属性或不应用属性的情况下均不起作用.
      • SecurityPermission is recommended by two different MS articles (first, second), although interestingly they do different things around explicit/implicit interface implementation
      • SecurityCritical is what Noda Time currently has, and is what this question's answer suggests
      • SecuritySafeCritical is somewhat suggested by Code Analysis rule messages
      • Without any attributes, Code Analysis rules are happy - with either SecurityPermission or SecurityCritical present, the rules tell you to remove the attributes - unless you do have AllowPartiallyTrustedCallers. Following the suggestions in either case doesn't help.
      • Noda Time has AllowPartiallyTrustedCallers applied to it; the example here doesn't work either with or without the attribute applied.

      如果将[assembly: SecurityRules(SecurityRuleSet.Level1)]添加到UntrustedCode程序集(并取消注释AllowPartiallyTrustedCallers属性),则代码将无例外运行,但是我认为这是一个很糟糕的解决方案,可能会妨碍其他代码.

      The code runs without an exception if I add [assembly: SecurityRules(SecurityRuleSet.Level1)] to the UntrustedCode assembly (and uncomment the AllowPartiallyTrustedCallers attribute), but I believe that's a poor solution to the problem that could hamper other code.

      我完全承认在这种情况下迷失了自己 .NET的安全性方面.那么可以我可以做什么来针对.NET 4.5和 但允许我的类型实现ISerializable并仍在 .NET Fiddle之类的环境?

      I fully admit to being pretty lost when it comes to this sort of security aspect of .NET. So what can I do to target .NET 4.5 and yet allow my types to implement ISerializable and still be used in environments such as .NET Fiddle?

      (虽然我的目标是.NET 4.5,但我相信是导致问题的原因是.NET 4.0安全策略的更改,因此还是标签.)

      (While I'm targeting .NET 4.5, I believe it's the .NET 4.0 security policy changes that caused the issue, hence the tag.)

      推荐答案

      根据 MSDN ,基本上,在.NET 4.0中,您不应将ISerializable用于部分受信任的代码,而应使用

      According to the MSDN, in .NET 4.0 basically you should not use ISerializable for partially trusted code, and instead you should use ISafeSerializationData

      https://docs.microsoft.com/引用zh-CN/dotnet/standard/serialization/custom-serialization

      重要

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

      In versions previous to .NET Framework 4.0, serialization of custom user data in a partially trusted assembly was accomplished using the GetObjectData. Starting with version 4.0, that method is marked with the SecurityCriticalAttribute attribute which prevents execution in partially trusted assemblies. To work around this condition, implement the ISafeSerializationData interface.

      所以如果您需要它,可能不是您想听的,但是我认为在继续使用ISerializable时没有任何解决方法(除了回到Level1安全性之外,您说您不希望这样做)不想).

      So probably not what you wanted to hear if you need it, but I don't think there's any way around it while keeping using ISerializable (other than going back to Level1 security, which you said you don't want to).

      PS:ISafeSerializationData文档指出,这只是为了例外,但似乎并没有那么具体,您可能需要试一试...我基本上无法使用示例代码对其进行测试(除了删除ISerializable有用,但您已经知道了)...您必须查看ISafeSerializationData是否足够适合您.

      PS: the ISafeSerializationData docs state that it is just for exceptions, but it doesn't seem all that specific, you may want to give it a shot... I basically can't test it with your sample code (other than removing ISerializable works, but you knew that already)... you'll have to see if ISafeSerializationData suits you enough.

      PS2:SecurityCritical属性不起作用,因为在以部分信任模式(级别2安全性上的 )加载程序集时,将忽略该属性.您可以在示例代码中看到它,如果在调用它之前在ExecuteUntrustedCode中调试ExecuteUntrustedCode变量,即使您标记了IsSecurityTransparenttrueIsSecurityCriticalfalse,它也将具有IsSecurityTransparenttrueIsSecurityCriticalfalse. SecurityCritical属性的方法)

      PS2: the SecurityCritical attribute doesn't work because it's ignored when the assembly is loaded in partial trust mode (on Level2 security). You can see it on your sample code, if you debug the target variable in ExecuteUntrustedCode right before invoking it, it'll have IsSecurityTransparent to true and IsSecurityCritical to false even if you mark the method with the SecurityCritical attribute)

      这篇关于如何在不违反继承安全规则的情况下在.NET 4+中实现ISerializable?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆