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 ISerializable
giao 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 / ẩnSecurityCritical
là 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
SecurityPermission
hoặcSecurityCritical
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àm cóAllowPartiallyTrustedCallers
. 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 UntrustedCode
hội đồng (và bỏ ghi chú AllowPartiallyTrustedCallers
thuộ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 ISerializable
và 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ẻ.)
AllowPartiallyTrustedCallers
nên làm các trick, nhưng nó dường như không tạo sự khác biệt