Làm cách nào để triển khai ISerializable trong .NET 4+ mà không vi phạm các quy tắc bảo mật kế thừa?


109

Bối cảnh: Noda Time chứa nhiều cấu trúc có thể tuần tự hóa. Mặc dù tôi không thích tuần tự hóa nhị phân, nhưng chúng tôi đã nhận được nhiều yêu cầu hỗ trợ nó, trở lại dòng thời gian 1.x. Chúng tôi hỗ trợ nó bằng cách triển khai ISerializablegiao diện.

Chúng tôi đã nhận được báo cáo sự cố gần đây về lỗi Noda Time 2.x trong .NET Fiddle . Mã tương tự sử dụng Noda Time 1.x hoạt động tốt. Ngoại lệ được ném ra là:

Quy tắc bảo mật kế thừa đã vi phạm khi ghi đè thành viên: 'NodaTime.Duration.System.Runtime.Serialization.ISerializable.GetObjectData (System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)'. Khả năng truy cập bảo mật của phương thức ghi đè phải phù hợp với khả năng truy cập bảo mật của phương thức được ghi đè.

Tôi đã thu hẹp điều này xuống khung được nhắm mục tiêu: 1.x mục tiêu .NET 3.5 (hồ sơ khách hàng); 2.x mục tiêu .NET 4.5. Chúng có sự khác biệt lớn về hỗ trợ PCL so với .NET Core và cấu trúc tệp dự án, nhưng có vẻ như điều này không liên quan.

Tôi đã quản lý để tái tạo điều này trong một dự án cục bộ, nhưng tôi chưa tìm ra giải pháp cho nó.

Các bước để tái tạo trong VS2017:

  • Tạo một giải pháp mới
  • Tạo một ứng dụng bảng điều khiển Windows cổ điển mới nhắm mục tiêu .NET 4.5.1. Tôi gọi nó là "CodeRunner".
  • Trong các thuộc tính của dự án, đi tới Ký và ký hợp ngữ bằng khóa mới. Bỏ chọn yêu cầu mật khẩu và sử dụng bất kỳ tên tệp khóa nào.
  • Dán mã sau để thay thế Program.cs. Đây là phiên bản viết tắt của mã trong mẫu Microsoft này . Tôi đã giữ nguyên tất cả các đường dẫn, vì vậy nếu bạn muốn quay lại mã đầy đủ hơn, bạn không cần phải thay đổi bất kỳ điều gì khác.

Mã:

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);
    }  
}
  • Tạo một dự án khác có tên là "UntrustedCode". Đây phải là một dự án Thư viện Lớp dành cho Máy tính để bàn Cổ điển.
  • Ký hợp đồng; bạn có thể sử dụng khóa mới hoặc khóa tương tự như cho CodeRunner. (Điều này một phần là để bắt chước tình huống Noda Time và một phần để giữ cho Phân tích mã vui vẻ.)
  • Dán mã sau vào Class1.cs(ghi đè lên những gì ở đó):

Mã:

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();
        }
    }
}

Chạy dự án CodeRunner đưa ra ngoại lệ sau (được định dạng lại để dễ đọc):

Ngoại lệ không được xử lý: System.Reflection.TargetInvocationException:
Ngoại lệ đã được ném ra bởi mục tiêu của một lời gọi.
--->
System.TypeLoadException: Các
quy tắc bảo mật kế thừa bị vi phạm khi ghi đè thành viên:
'UntrustedCode.CustomStruct.System.Runtime.Serialization.ISerializable.GetObjectData (...).
Khả năng truy cập bảo mật
của phương thức ghi đè phải phù hợp với khả năng truy cập bảo mật của phương thức được ghi đè.

Các thuộc tính đã nhận xét hiển thị những điều tôi đã thử:

  • SecurityPermissionđược đề xuất bởi hai bài báo MS khác nhau ( thứ nhất , thứ hai ), mặc dù thú vị là chúng làm những việc khác nhau xung quanh việc triển khai giao diện rõ ràng / ẩn
  • SecurityCriticallà những gì Noda Time hiện có và là câu trả lời của câu hỏi này gợi ý
  • SecuritySafeCritical phần nào được gợi ý bởi thông báo quy tắc Phân tích mã
  • Nếu không có bất kỳ thuộc tính, quy tắc phân tích Mã là hạnh phúc - với một trong hai SecurityPermissionhoặc SecurityCritical hiện tại, các quy tắc cho bạn để loại bỏ các thuộc tính - trừ khi bạn làmAllowPartiallyTrustedCallers. Làm theo các đề xuất trong cả hai trường hợp không giúp ích gì.
  • Noda Time đã AllowPartiallyTrustedCallersáp dụng cho nó; ví dụ ở đây không hoạt động có hoặc không có thuộc tính được áp dụng.

Mã chạy mà không có ngoại lệ nếu tôi thêm [assembly: SecurityRules(SecurityRuleSet.Level1)]vào UntrustedCodehội đồng (và bỏ ghi chú AllowPartiallyTrustedCallersthuộc tính), nhưng tôi tin rằng đó là một giải pháp kém cho vấn đề có thể cản trở mã khác.

Tôi hoàn toàn thừa nhận là đã khá mất hứng khi nói đến khía cạnh bảo mật này của .NET. Vậy tôi có thể làm gì để nhắm mục tiêu .NET 4.5 mà vẫn cho phép các kiểu của tôi triển khai ISerializablevà vẫn được sử dụng trong các môi trường chẳng hạn như .NET Fiddle?

(Trong khi tôi đang nhắm mục tiêu .NET 4.5, tôi tin rằng những thay đổi về chính sách bảo mật .NET 4.0 đã gây ra sự cố, do đó là thẻ.)


Điều thú vị đủ, lời giải thích này của thay đổi đối với mô hình bảo mật trong 4.0 cho thấy rằng chỉ đơn giản là loại bỏ AllowPartiallyTrustedCallersnên làm các trick, nhưng nó dường như không tạo sự khác biệt
Mathias R. Jessen

Câu trả lời:


56

Theo MSDN , trong .NET 4.0 về cơ bản bạn không nên sử dụng ISerializablecho mã tin cậy một phần và thay vào đó bạn nên sử dụng ISafeSerializationData

Trích dẫn từ https://docs.microsoft.com/en-us/dotnet/standard/serialization/custom-serialization

Quan trọng

Trong các phiên bản trước .NET Framework 4.0, việc tuần tự hóa dữ liệu người dùng tùy chỉnh trong một hội đồng đáng tin cậy một phần đã được thực hiện bằng cách sử dụng GetObjectData. Bắt đầu với phiên bản 4.0, phương pháp đó được đánh dấu bằng thuộc tính SecurityCriticalAttribute ngăn việc thực thi trong các hội đồng đáng tin cậy một phần. Để khắc phục tình trạng này, hãy triển khai giao diện ISafeSerializationData.

Vì vậy, có thể không phải là những gì bạn muốn nghe nếu bạn cần, nhưng tôi không nghĩ rằng có bất kỳ cách nào để giải quyết vấn đề đó khi tiếp tục sử dụng ISerializable(ngoài việc quay lại Level1bảo mật, điều mà bạn đã nói là bạn không muốn).

Tái bút: ISafeSerializationDatatài liệu nói rằng nó chỉ dành cho các trường hợp ngoại lệ, nhưng có vẻ như nó không phải là tất cả cụ thể, bạn có thể muốn thử ... Về cơ bản tôi không thể kiểm tra nó với mã mẫu của bạn (ngoại trừ việc xóa ISerializablecác tác phẩm, nhưng bạn đã biết điều đó rồi) ... bạn sẽ phải xem liệu ISafeSerializationDatacó đủ phù hợp với mình không.

PS2: SecurityCriticalthuộc tính không hoạt động vì nó bị bỏ qua khi lắp ráp được tải ở chế độ tin cậy một phần ( trên bảo mật Cấp 2 ). Bạn có thể nhìn thấy nó trên mẫu mã của bạn, nếu bạn gỡ lỗi các targetbiến trong ExecuteUntrustedCodengay trước khi gọi nó, nó sẽ phải IsSecurityTransparentđến trueIsSecurityCriticalđể falsengay cả khi bạn đánh dấu các phương pháp với các SecurityCriticalthuộc tính)


Aha - cảm ơn vì lời giải thích. Điều đáng xấu hổ là ngoại lệ rất dễ gây hiểu lầm ở đây. Sẽ cần phải làm việc ra những gì để làm ...
Jon Skeet

@JonSkeet Thành thực mà nói, tôi muốn mương nhị phân serialization alltogether ... nhưng tôi hiểu userbase của bạn có thể không thích nó
JCL

Tôi nghĩ chúng ta sẽ phải làm điều đó - có nghĩa là chuyển sang phiên bản 3.0. Tuy nhiên, nó có những lợi ích khác ... Tôi sẽ cần tham khảo ý kiến ​​của cộng đồng Noda Time.
Jon Skeet

12
@JonSkeet btw, nếu bạn quan tâm, bài viết này giải thích sự khác biệt giữa bảo mật cấp 1 và cấp 2 (và TẠI SAO nó không hoạt động)
Jcl

8

Câu trả lời được chấp nhận thuyết phục đến mức tôi gần như tin rằng đây không phải là một lỗi. Nhưng sau khi thực hiện một số thử nghiệm bây giờ tôi có thể nói rằng bảo mật Cấp độ 2 là một mớ hỗn độn; ít nhất, một cái gì đó thực sự là tanh.

Một vài ngày trước, tôi đã gặp vấn đề tương tự với các thư viện của mình. Tôi nhanh chóng tạo một bài kiểm tra đơn vị; tuy nhiên, tôi không thể tái tạo sự cố mà tôi đã gặp phải trong .NET Fiddle, trong khi cùng một đoạn mã "thành công" đã ném ngoại lệ vào ứng dụng bảng điều khiển. Cuối cùng, tôi đã tìm ra hai cách kỳ lạ để khắc phục vấn đề.

TL; DR : Hóa ra là nếu bạn sử dụng loại nội bộ của thư viện đã sử dụng trong dự án tiêu dùng của mình, thì mã tin cậy một phần sẽ hoạt động như mong đợi: nó có thể khởi tạo một ISerializabletriển khai (và mã quan trọng bảo mật không thể được gọi trực tiếp, nhưng xem bên dưới). Hoặc, điều nực cười hơn nữa, bạn có thể thử tạo lại hộp cát nếu nó không hoạt động lần đầu tiên ...

Nhưng hãy xem một số mã.

ClassLibrary.dll:

Hãy tách hai trường hợp: một trường hợp dành cho lớp thông thường có nội dung quan trọng về bảo mật và một trường hợp ISerializabletriển khai:

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) { }
}

Một cách để khắc phục vấn đề này là sử dụng một loại bên trong từ lắp ráp tiêu dùng. Loại nào cũng được; bây giờ tôi xác định một thuộc tính:

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

Và các thuộc tính liên quan được áp dụng cho assembly:

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

Ký hợp đồng, áp dụng khóa cho InternalsVisibleTothuộc tính và chuẩn bị cho dự án thử nghiệm:

UnitTest.dll (sử dụng NUnit và ClassLibrary):

Để sử dụng thủ thuật nội bộ, tổ hợp thử nghiệm cũng phải được ký. Thuộc tính hội:

// 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]

Lưu ý : Thuộc tính có thể được áp dụng ở bất kỳ đâu. Trong trường hợp của tôi, đó là một phương pháp trong một lớp thử nghiệm ngẫu nhiên, tôi mất vài ngày để tìm ra.

Lưu ý 2 : Nếu bạn chạy tất cả các phương pháp thử nghiệm cùng nhau, có thể xảy ra trường hợp thử nghiệm sẽ vượt qua.

Khung của lớp thử nghiệm:

[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;
    }
}

Và chúng ta hãy xem từng trường hợp thử nghiệm một

Trường hợp 1: Triển khai ISerializable

Vấn đề tương tự như trong câu hỏi. Kiểm tra vượt qua nếu

  • InternalTypeReferenceAttribute được áp dụng
  • hộp cát được cố gắng tạo nhiều lần (xem mã)
  • hoặc, nếu tất cả các trường hợp thử nghiệm được thực thi cùng một lúc và đây không phải là trường hợp đầu tiên

Nếu không, sẽ có Inheritance security rules violated while overriding member...ngoại lệ hoàn toàn không thích hợp khi bạn khởi tạo 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);
    }

}

Trường hợp 2: Lớp học thông thường với các thành viên quan trọng về bảo mật

Thử nghiệm vượt qua trong các điều kiện tương tự như thử nghiệm đầu tiên. Tuy nhiên, vấn đề hoàn toàn khác ở đây: một mã tin cậy một phần có thể truy cập trực tiếp vào thành viên quan trọng về bảo mật .

[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());
    }
}

Trường hợp 3-4: Các phiên bản tin cậy hoàn toàn của trường hợp 1-2

Vì lợi ích của sự đầy đủ ở đây là các trường hợp tương tự như các trường hợp ở trên được thực thi trong một miền hoàn toàn tin cậy. Nếu bạn loại bỏ [assembly: AllowPartiallyTrustedCallers]các bài kiểm tra không thành công vì sau đó bạn có thể truy cập trực tiếp mã quan trọng (vì các phương pháp không còn minh bạch theo mặc định nữa).

[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);
}

Phần kết:

Tất nhiên, điều này sẽ không giải quyết được vấn đề của bạn với .NET Fiddle. Nhưng bây giờ tôi sẽ rất ngạc nhiên nếu nó không phải là một lỗi trong khuôn khổ.

Câu hỏi lớn nhất đối với tôi bây giờ là phần được trích dẫn trong câu trả lời được chấp nhận. Làm thế nào mà họ lại ra tay với điều vô nghĩa này? Các ISafeSerializationDatarõ ràng không phải là một giải pháp cho bất cứ điều gì: nó được sử dụng độc quyền bởi các cơ sở Exceptionlớp và nếu bạn đăng ký các SerializeObjectStatesự kiện (tại sao không phải là một phương pháp overridable?), Sau đó nhà nước cũng sẽ được tiêu thụ bởi các Exception.GetObjectDatacuối cùng.

Các AllowPartiallyTrustedCallers/ SecurityCritical/ SecuritySafeCriticalbộ ba thuộc tính được thiết kế cho chính xác việc sử dụng hiển thị ở trên. Có vẻ như hoàn toàn vô lý đối với tôi rằng một mã được tin cậy một phần thậm chí không thể khởi tạo một loại bất kể nỗ lực sử dụng các thành viên quan trọng bảo mật của nó. Nhưng điều vô nghĩa thậm chí còn lớn hơn ( thực ra là một lỗ hổng bảo mật ) là mã được tin cậy một phần có thể truy cập trực tiếp vào phương pháp quan trọng bảo mật (xem trường hợp 2 ) trong khi điều này bị cấm đối với các phương pháp minh bạch ngay cả từ một miền hoàn toàn tin cậy.

Vì vậy, nếu dự án tiêu dùng của bạn là một thử nghiệm hoặc một lắp ráp nổi tiếng khác, thì thủ thuật nội bộ có thể được sử dụng một cách hoàn hảo. Đối với .NET Fiddle và các môi trường hộp cát ngoài đời thực khác, giải pháp duy nhất là hoàn nguyên trở lại cho SecurityRuleSet.Level1đến khi điều này được Microsoft khắc phục.


Cập nhật: Một vé cho nhà phát triển cộng đồng đã được tạo ra cho vấn đề này.


2

Theo MSDN xem:

Làm thế nào để sửa chữa vi phạm?

Để khắc phục vi phạm quy tắc này, hãy làm cho phương thức GetObjectData hiển thị và có thể ghi đè và đảm bảo tất cả các trường phiên bản đều được đưa vào quy trình tuần tự hóa hoặc được đánh dấu rõ ràng bằng thuộc tính NonSerializedAttribute .

Ví dụ sau khắc phục hai lỗi vi phạm trước đó bằng cách cung cấp triển khai có thể ghi đè ISerializable.GetObjectData trên lớp Sách và bằng cách cung cấp triển khai ISerializable.GetObjectData trên lớp Thư viện.

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
Bài viết mà bạn đã liên kết là dành cho CA2240, không được kích hoạt - mã không vi phạm. Đó là một cấu trúc, vì vậy nó được niêm phong hiệu quả; nó không có bất kỳ trường nào; nó thực hiện GetObjectDatamột cách rõ ràng, nhưng làm như vậy một cách hoàn toàn không giúp ích gì.
Jon Skeet

15
Chắc chắn rồi, và cảm ơn bạn đã thử - nhưng tôi đang giải thích tại sao nó không hoạt động. (Và như một đề nghị - cho một cái gì đó khó khăn như thế này, nơi mà các câu hỏi bao gồm một ví dụ có thể kiểm chứng, đó là một ý tưởng tốt để cố gắng áp dụng sửa chữa đề nghị và xem liệu nó thực sự giúp.)
Jon Skeet
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.