Devralma güvenlik kurallarını ihlal etmeden .NET 4 + 'da ISerializable'ı nasıl uygulayabilirim?


110

Arka plan: Noda Time birçok serileştirilebilir yapı içerir. İkili serileştirmeyi sevmesem de, 1.x zaman çizelgesinde onu desteklemek için birçok istek aldık. ISerializableArayüzü uygulayarak bunu destekliyoruz .

Noda Time 2.x'in .NET Fiddle'da başarısız olduğuna dair yeni bir sorun raporu aldık . Noda Time 1.x kullanan aynı kod iyi çalışıyor. Atılan istisna şudur:

Üyeyi geçersiz kılarken devralma güvenlik kuralları ihlal edildi: 'NodaTime.Duration.System.Runtime.Serialization.ISerializable.GetObjectData (System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)'. Geçersiz kılma yönteminin güvenlik erişilebilirliği, geçersiz kılınan yöntemin güvenlik erişilebilirliğiyle eşleşmelidir.

Bunu hedeflenen çerçeveye daralttım: 1.x .NET 3.5'i (istemci profili) hedefliyor; 2.x, .NET 4.5'i hedefler. Destek PCL ile .NET Core ve proje dosya yapısı açısından büyük farklılıkları var, ancak bu alakasız görünüyor.

Bunu yerel bir projede yeniden üretmeyi başardım, ancak buna bir çözüm bulamadım.

VS2017'de yeniden oluşturma adımları:

  • Yeni bir çözüm oluşturun
  • .NET 4.5.1'i hedefleyen yeni bir klasik Windows konsol uygulaması oluşturun. Ben buna "CodeRunner" adını verdim.
  • Proje özelliklerinde, İmzalama'ya gidin ve montajı yeni bir anahtarla imzalayın. Parola gereksiniminin işaretini kaldırın ve herhangi bir anahtar dosyası adı kullanın.
  • Değiştirmek için aşağıdaki kodu yapıştırın Program.cs. Bu, bu Microsoft örneğindeki kodun kısaltılmış bir sürümüdür . Tüm yolları aynı tuttum, bu yüzden daha tam koda geri dönmek isterseniz, başka hiçbir şeyi değiştirmenize gerek kalmaz.

Kod:

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" adlı başka bir proje oluşturun. Bu bir Klasik Masaüstü Sınıf Kitaplığı projesi olmalıdır.
  • Montajı imzalayın; yeni bir anahtar veya CodeRunner ile aynı olanı kullanabilirsiniz. (Bu kısmen Noda Saati durumunu taklit etmek ve kısmen Kod Analizini mutlu etmek içindir.)
  • Aşağıdaki kodu içine yapıştırın Class1.cs(orada olanın üzerine yazın):

Kod:

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 projesini çalıştırmak aşağıdaki istisnayı verir (okunabilirlik için yeniden biçimlendirilmiş):

İşlenmemiş Özel Durum: System.Reflection.TargetInvocationException:
Bir çağrının hedefi tarafından özel durum oluşturuldu.
--->
System.TypeLoadException:
Üye geçersiz kılarken devralma güvenlik kuralları ihlal edildi:
'UntrustedCode.CustomStruct.System.Runtime.Serialization.ISerializable.GetObjectData (...).
Geçersiz kılma yönteminin güvenlik
erişilebilirliği , geçersiz kılınan yöntemin güvenlik erişilebilirliğiyle eşleşmelidir .

Yorumlanmış öznitelikler denediğim şeyleri gösterir:

  • SecurityPermissioniki farklı MS makalesi tarafından önerilmektedir ( birinci , ikinci ), ancak ilginç bir şekilde açık / örtük arayüz uygulaması etrafında farklı şeyler yaparlar
  • SecurityCriticalNoda Time'ın şu anda sahip olduğu ve bu sorunun cevabının önerdiği
  • SecuritySafeCritical Kod Analizi kural mesajları tarafından bir şekilde önerilmektedir
  • Olmadan herhangi nitelikler, Kod Analizi kuralları mutluyuz - biriyle SecurityPermissionveya SecurityCritical mevcut kurallar özelliklerini kaldırmak için size - Eğer sürece yapmak zorunda AllowPartiallyTrustedCallers. Her iki durumda da önerileri takip etmek yardımcı olmaz.
  • Noda Time AllowPartiallyTrustedCallersona uyguladı; buradaki örnek, öznitelik uygulanmış veya uygulanmamış olarak çalışmaz.

Bir istisnasız kod çalışır ben eklerseniz [assembly: SecurityRules(SecurityRuleSet.Level1)]için UntrustedCodemontaj (ve yorumsuz AllowPartiallyTrustedCallersözniteliği), ama bu başka bir kod engel olacağı sorununa fakir çözüm olduğuna inanıyoruz.

.NET'in bu tür bir güvenlik yönü söz konusu olduğunda oldukça kaybolduğumu tamamen itiraf ediyorum. Öyleyse , .NET 4.5'i hedeflemek ISerializableve yine de türlerimin .NET Fiddle gibi ortamlarda uygulanmasına ve kullanılmasına izin vermek için ne yapabilirim ?

(.NET 4.5'i hedeflerken, soruna neden olanın .NET 4.0 güvenlik politikası değişikliklerinin, dolayısıyla etiketinin olduğunu düşünüyorum.)


Yeterince ilginç bir şekilde, 4.0'daki güvenlik modelindeki değişikliklerin bu açıklaması, basitçe kaldırmanın AllowPartiallyTrustedCallershile yapması gerektiğini öne sürüyor , ancak bu bir fark yaratmıyor
Mathias

Yanıtlar:


56

Göre MSDN , .NET 4.0 temelde kullanmamalısınız ISerializablekısmen güvenilen kod ve bunun yerine kullanmanız gereken ISafeSerializationData

Https://docs.microsoft.com/en-us/dotnet/standard/serialization/custom-serialization adresinden alıntı

Önemli

.NET Framework 4.0'dan önceki sürümlerde, kısmen güvenilir bir derlemede özel kullanıcı verilerinin serileştirilmesi GetObjectData kullanılarak gerçekleştiriliyordu. Sürüm 4.0'dan başlayarak, bu yöntem, kısmen güvenilen derlemelerde yürütmeyi engelleyen SecurityCriticalAttribute özniteliğiyle işaretlenmiştir. Bu koşula geçici bir çözüm bulmak için ISafeSerializationData arabirimini uygulayın.

Muhtemelen ihtiyacın olursa duymak istediğin şey değil, ama kullanmaya ISerializabledevam ederken etrafta herhangi bir yol olduğunu sanmıyorum ( Level1istemediğini söylediğin güvenliğe geri dönmek dışında ).

Not: ISafeSerializationDataDokümanlar, bunun sadece istisnalar için olduğunu belirtiyor, ancak o kadar özel görünmüyor, bir şans vermek isteyebilirsiniz ... Temelde örnek kodunuzla test edemiyorum ( ISerializableçalışmaları kaldırmak dışında , ama bunu zaten biliyordun) ... ISafeSerializationDatasana yeterince uyup uymadığını görmen gerekecek .

PS2: SecurityCriticalöznitelik çalışmaz çünkü derleme kısmi güven modunda yüklendiğinde ( Seviye2 güvenliğinde ) yok sayılır . Sen, örnek kod üzerinde görebilirsiniz, debug eğer targetiçinde değişken ExecuteUntrustedCodebunu çağırmadan önce sağa, bu gerekecek IsSecurityTransparentiçin trueve IsSecurityCriticaliçin falsesizinle yöntemini işaretlemek bile SecurityCritical) özniteliği


Aha - açıklama için teşekkürler. Utanç, istisna burada çok yanıltıcı. Ne yapacağını
Jon Skeet

@JonSkeet Dürüst olmak gerekirse, ikili serileştirmeyi tamamen ortadan kaldırırdım ... ama kullanıcı
tabanınızın bundan

Sanırım bunu yapmamız gerekecek - bu da v3.0'a geçmek anlamına geliyor. Yine de başka faydaları da var ... Noda Time topluluğuna danışmam gerekecek.
Jon Skeet

12
@JonSkeet btw, ilgileniyorsanız, bu makale seviye 1 ve seviye 2 güvenlik arasındaki farkları açıklıyor (ve NEDEN çalışmıyor)
Jcl

8

Kabul edilen cevap o kadar inandırıcı ki neredeyse bunun bir hata olmadığına inandım. Ancak bazı deneyler yaptıktan sonra artık Level2 güvenliğinin tam bir karmaşa olduğunu söyleyebilirim; en azından bir şey gerçekten şüpheli.

Birkaç gün önce kütüphanelerimle aynı sorunla karşılaştım. Hızlı bir şekilde bir birim testi oluşturdum; ancak, aynı kod bir konsol uygulamasında istisnayı "başarıyla" atarken, .NET Fiddle'da yaşadığım sorunu yeniden oluşturamadım. Sonunda sorunun üstesinden gelmenin iki garip yolunu buldum.

TL; DR : Tüketici projenizde kullanılan kitaplığın dahili bir türünü kullanırsanız, kısmen güvenilen kodun beklendiği gibi çalıştığı ortaya çıkıyor: bir ISerializableuygulamayı başlatabilir (ve bir güvenlik kritik kodu doğrudan çağrılamaz, ancak aşağıya bakın). Ya da daha da gülünç olan, ilk kez çalışmadıysa korumalı alanı yeniden oluşturmayı deneyebilirsiniz ...

Ama biraz kod görelim.

ClassLibrary.dll:

İki durumu ayıralım: biri güvenlik açısından kritik içeriğe sahip normal bir sınıf için, diğeri ISerializableuygulama:

public class CriticalClass
{
    public void SafeCode() { }

    [SecurityCritical]
    public void CriticalCode() { }

    [SecuritySafeCritical]
    public void SafeEntryForCriticalCode() => CriticalCode();
}

[Serializable]
public class SerializableCriticalClass : CriticalClass, ISerializable
{
    public SerializableCriticalClass() { }

    private SerializableCriticalClass(SerializationInfo info, StreamingContext context) { }

    [SecurityCritical]
    public void GetObjectData(SerializationInfo info, StreamingContext context) { }
}

Sorunun üstesinden gelmenin bir yolu, tüketici montajından bir dahili tip kullanmaktır. Herhangi bir tür bunu yapacak; şimdi bir öznitelik tanımlıyorum:

[AttributeUsage(AttributeTargets.All)]
internal class InternalTypeReferenceAttribute : Attribute
{
    public InternalTypeReferenceAttribute() { }
}

Ve montaja uygulanan ilgili özellikler:

[assembly: InternalsVisibleTo("UnitTest, PublicKey=<your public key>")]
[assembly: AllowPartiallyTrustedCallers]
[assembly: SecurityRules(SecurityRuleSet.Level2, SkipVerificationInFullTrust = true)]

Montajı imzalayın, InternalsVisibleToniteliğe anahtarı uygulayın ve test projesine hazırlanın:

UnitTest.dll (NUnit ve ClassLibrary kullanır):

Dahili numarayı kullanmak için test düzeneği de imzalanmalıdır. Montaj özellikleri:

// Just to make the tests security transparent by default. This helps to test the full trust behavior.
[assembly: AllowPartiallyTrustedCallers] 

// !!! Comment this line out and the partial trust test cases may fail for the fist time !!!
[assembly: InternalTypeReference]

Not : Özellik herhangi bir yere uygulanabilir. Benim durumumda, rastgele bir test sınıfındaki bir yöntem üzerindeydi, bulmam birkaç gün sürdü.

Not 2 : Tüm test yöntemlerini birlikte çalıştırırsanız, testler geçebilir.

Test sınıfının iskeleti:

[TestFixture]
public class SecurityCriticalAccessTest
{
    private partial class Sandbox : MarshalByRefObject
    {
    }

    private static AppDomain CreateSandboxDomain(params IPermission[] permissions)
    {
        var evidence = new Evidence(AppDomain.CurrentDomain.Evidence);
        var permissionSet = GetPermissionSet(permissions);
        var setup = new AppDomainSetup
        {
            ApplicationBase = AppDomain.CurrentDomain.BaseDirectory,
        };

        var assemblies = AppDomain.CurrentDomain.GetAssemblies();
        var strongNames = new List<StrongName>();
        foreach (Assembly asm in assemblies)
        {
            AssemblyName asmName = asm.GetName();
            strongNames.Add(new StrongName(new StrongNamePublicKeyBlob(asmName.GetPublicKey()), asmName.Name, asmName.Version));
        }

        return AppDomain.CreateDomain("SandboxDomain", evidence, setup, permissionSet, strongNames.ToArray());
    }

    private static PermissionSet GetPermissionSet(IPermission[] permissions)
    {
        var evidence = new Evidence();
        evidence.AddHostEvidence(new Zone(SecurityZone.Internet));
        var result = SecurityManager.GetStandardSandbox(evidence);
        foreach (var permission in permissions)
            result.AddPermission(permission);
        return result;
    }
}

Ve test durumlarını tek tek görelim

Durum 1: ISerializable uygulama

Sorudakiyle aynı konu. Test başarılı olursa

  • InternalTypeReferenceAttribute uygulandı
  • korumalı alan birden çok kez oluşturulmaya çalışılır (koda bakın)
  • veya tüm test senaryoları aynı anda yürütülürse ve bu ilk değilse

Aksi takdirde, Inheritance security rules violated while overriding member...somutlaştırdığınızda tamamen uygunsuz bir istisna ortaya çıkar SerializableCriticalClass.

[Test]
[SecuritySafeCritical] // for Activator.CreateInstance
public void SerializableCriticalClass_PartialTrustAccess()
{
    var domain = CreateSandboxDomain(
        new SecurityPermission(SecurityPermissionFlag.SerializationFormatter), // BinaryFormatter
        new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)); // Assert.IsFalse
    var handle = Activator.CreateInstance(domain, Assembly.GetExecutingAssembly().FullName, typeof(Sandbox).FullName);
    var sandbox = (Sandbox)handle.Unwrap();
    try
    {
        sandbox.TestSerializableCriticalClass();
        return;
    }
    catch (Exception e)
    {
        // without [InternalTypeReference] it may fail for the first time
        Console.WriteLine($"1st try failed: {e.Message}");
    }

    domain = CreateSandboxDomain(
        new SecurityPermission(SecurityPermissionFlag.SerializationFormatter), // BinaryFormatter
        new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)); // Assert.IsFalse
    handle = Activator.CreateInstance(domain, Assembly.GetExecutingAssembly().FullName, typeof(Sandbox).FullName);
    sandbox = (Sandbox)handle.Unwrap();
    sandbox.TestSerializableCriticalClass();

    Assert.Inconclusive("Meh... succeeded only for the 2nd try");
}

private partial class Sandbox
{
    public void TestSerializableCriticalClass()
    {
        Assert.IsFalse(AppDomain.CurrentDomain.IsFullyTrusted);

        // ISerializable implementer can be created.
        // !!! May fail for the first try if the test does not use any internal type of the library. !!!
        var critical = new SerializableCriticalClass();

        // Critical method can be called via a safe method
        critical.SafeEntryForCriticalCode();

        // Critical method cannot be called directly by a transparent method
        Assert.Throws<MethodAccessException>(() => critical.CriticalCode());
        Assert.Throws<MethodAccessException>(() => critical.GetObjectData(null, new StreamingContext()));

        // BinaryFormatter calls the critical method via a safe route (SerializationFormatter permission is required, though)
        new BinaryFormatter().Serialize(new MemoryStream(), critical);
    }

}

Durum 2: Güvenlik açısından kritik üyelere sahip normal sınıf

Test, birincisi ile aynı koşullar altında geçer. Ancak burada sorun tamamen farklıdır: kısmen güvenilen bir kod, güvenlik açısından kritik bir üyeye doğrudan erişebilir .

[Test]
[SecuritySafeCritical] // for Activator.CreateInstance
public void CriticalClass_PartialTrustAccess()
{
    var domain = CreateSandboxDomain(
        new ReflectionPermission(ReflectionPermissionFlag.MemberAccess), // Assert.IsFalse
        new EnvironmentPermission(PermissionState.Unrestricted)); // Assert.Throws (if fails)
    var handle = Activator.CreateInstance(domain, Assembly.GetExecutingAssembly().FullName, typeof(Sandbox).FullName);
    var sandbox = (Sandbox)handle.Unwrap();
    try
    {
        sandbox.TestCriticalClass();
        return;
    }
    catch (Exception e)
    {
        // without [InternalTypeReference] it may fail for the first time
        Console.WriteLine($"1st try failed: {e.Message}");
    }

    domain = CreateSandboxDomain(
        new ReflectionPermission(ReflectionPermissionFlag.MemberAccess)); // Assert.IsFalse
    handle = Activator.CreateInstance(domain, Assembly.GetExecutingAssembly().FullName, typeof(Sandbox).FullName);
    sandbox = (Sandbox)handle.Unwrap();
    sandbox.TestCriticalClass();

    Assert.Inconclusive("Meh... succeeded only for the 2nd try");
}

private partial class Sandbox
{
    public void TestCriticalClass()
    {
        Assert.IsFalse(AppDomain.CurrentDomain.IsFullyTrusted);

        // A type containing critical methods can be created
        var critical = new CriticalClass();

        // Critical method can be called via a safe method
        critical.SafeEntryForCriticalCode();

        // Critical method cannot be called directly by a transparent method
        // !!! May fail for the first time if the test does not use any internal type of the library. !!!
        // !!! Meaning, a partially trusted code has more right than a fully trusted one and is       !!!
        // !!! able to call security critical method directly.                                        !!!
        Assert.Throws<MethodAccessException>(() => critical.CriticalCode());
    }
}

Durum 3-4: Durum 1-2'nin tam güven sürümleri

Eksiksizlik adına, burada tamamen güvenilir bir alanda yürütülen yukarıdaki durumlarla aynı durumlar vardır. [assembly: AllowPartiallyTrustedCallers]Testleri kaldırırsanız , çünkü kritik koda doğrudan erişebilirsiniz (yöntemler artık varsayılan olarak şeffaf olmadığından).

[Test]
public void CriticalClass_FullTrustAccess()
{
    Assert.IsTrue(AppDomain.CurrentDomain.IsFullyTrusted);

    // A type containing critical methods can be created
    var critical = new CriticalClass();

    // Critical method cannot be called directly by a transparent method
    Assert.Throws<MethodAccessException>(() => critical.CriticalCode());

    // Critical method can be called via a safe method
    critical.SafeEntryForCriticalCode();
}

[Test]
public void SerializableCriticalClass_FullTrustAccess()
{
    Assert.IsTrue(AppDomain.CurrentDomain.IsFullyTrusted);

    // ISerializable implementer can be created
    var critical = new SerializableCriticalClass();

    // Critical method cannot be called directly by a transparent method (see also AllowPartiallyTrustedCallersAttribute)
    Assert.Throws<MethodAccessException>(() => critical.CriticalCode());
    Assert.Throws<MethodAccessException>(() => critical.GetObjectData(null, default(StreamingContext)));

    // Critical method can be called via a safe method
    critical.SafeEntryForCriticalCode();

    // BinaryFormatter calls the critical method via a safe route
    new BinaryFormatter().Serialize(new MemoryStream(), critical);
}

Son Söz:

Tabii ki, bu .NET Fiddle ile olan probleminizi çözmeyecektir. Ama şimdi çerçevede bir hata olmasa çok şaşırırdım.

Şu anda benim için en büyük soru, kabul edilen cevapta alıntılanan kısım. Bu saçmalıkla nasıl ortaya çıktılar? ISafeSerializationDataAçıkça bir şey için bir çözüm değildir: o baz tarafından özel olarak kullanılan Exceptionabone eğer sınıf ve SerializeObjectStateolay (geçersiz kılınabilir bir yöntem olmadığını neden?), Sonra devlet de tüketilen edilecektir Exception.GetObjectDatasonunda.

AllowPartiallyTrustedCallers/ SecurityCritical/ SecuritySafeCriticalNiteliklerin üçlüsünün yukarıda gösterilen tam olarak kullanım için tasarlanmıştır. Kısmen güvenilen bir kodun, güvenlik açısından kritik üyelerini kullanma girişimi ne olursa olsun bir türü başlatamayacağı bana tamamen saçma geliyor. Ancak, kısmen güvenilen bir kodun güvenlik açısından kritik bir yönteme doğrudan erişmesi daha da büyük bir saçmadır ( aslında bir güvenlik açığı ) (bkz. Durum 2 ), oysa bu tamamen güvenilir bir alandan bile şeffaf yöntemler için yasaktır.

Dolayısıyla, tüketici projeniz bir test veya iyi bilinen başka bir montaj ise, o zaman dahili numara mükemmel bir şekilde kullanılabilir. .NET Fiddle ve diğer gerçek hayattaki korumalı ortamlar için tek çözüm, SecurityRuleSet.Level1Microsoft tarafından düzeltilene kadar geri dönmektir .


Güncelleme: Sorun için bir Geliştirici Topluluğu bileti oluşturuldu.


2

Göre MSDN bakın:

İhlaller Nasıl Giderilir?

Bu kuralın ihlalini gidermek için GetObjectData yöntemini görünür ve geçersiz kılın ve tüm örnek alanlarının serileştirme işlemine dahil edildiğinden veya NonSerializedAttribute özniteliğiyle açıkça işaretlendiğinden emin olun .

Aşağıdaki örnek , Book sınıfında ISerializable.GetObjectData öğesinin geçersiz kılınabilen bir uygulamasını sağlayarak ve Library sınıfında ISerializable.GetObjectData uygulamasının bir uygulamasını sağlayarak önceki iki ihlali düzeltir.

using System;
using System.Security.Permissions;
using System.Runtime.Serialization;

namespace Samples2
{
    [Serializable]
    public class Book : ISerializable
    {
        private readonly string _Title;

        public Book(string title)
        {
            if (title == null)
                throw new ArgumentNullException("title");

            _Title = title;
        }

        protected Book(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
                throw new ArgumentNullException("info");

            _Title = info.GetString("Title");
        }

        public string Title
        {
            get { return _Title; }
        }

        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
        protected virtual void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("Title", _Title);
        }

        [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)]
        void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context)
        {
            if (info == null)
                throw new ArgumentNullException("info");

            GetObjectData(info, context);
        }
    }

    [Serializable]
    public class LibraryBook : Book
    {
        private readonly DateTime _CheckedOut;

        public LibraryBook(string title, DateTime checkedOut)
            : base(title)
        {
            _CheckedOut = checkedOut;
        }

        protected LibraryBook(SerializationInfo info, StreamingContext context)
            : base(info, context)
        {
            _CheckedOut = info.GetDateTime("CheckedOut");
        }

        public DateTime CheckedOut
        {
            get { return _CheckedOut; }
        }

        [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)]
        protected override void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            base.GetObjectData(info, context);

            info.AddValue("CheckedOut", _CheckedOut);
        }
    }
}

2
Bağlandığınız makale, ateşlenmeyen CA2240 için - kod onu ihlal etmiyor. Bu bir yapı, bu yüzden etkili bir şekilde mühürlendi; herhangi bir alanı yok; GetObjectDataaçıkça uygular , ancak bunu dolaylı olarak yapmak yardımcı olmaz.
Jon Skeet

15
Tabi, ve çalıştıkları için teşekkür - ama neden anlatıyorum değil işi. (Ve bir öneri olarak - sorunun doğrulanabilir bir örnek içerdiği böyle zor bir şey için, önerilen düzeltmeyi uygulamaya çalışmak ve bunun gerçekten yardımcı olup olmadığını görmek iyi bir fikirdir .)
Jon Skeet
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.