ความเป็นมา: Noda Timeมีโครงสร้างที่ต่อเนื่องกันได้มากมาย แม้ว่าฉันไม่ชอบการทำให้เป็นอนุกรมไบนารี แต่เราก็ได้รับคำขอให้สนับสนุนมากมายกลับมาในไทม์ไลน์ 1.x เราสนับสนุนโดยใช้ISerializable
อินเทอร์เฟซ
เราได้รับเมื่อเร็ว ๆ นี้รายงานปัญหาของ Noda เวลา 2.x ล้มเหลวภายใน .NET ซอ รหัสเดียวกันที่ใช้ Noda Time 1.x ทำงานได้ดี ข้อยกเว้นที่เกิดขึ้นคือ:
กฎความปลอดภัยในการสืบทอดถูกละเมิดในขณะที่แทนที่สมาชิก: 'NodaTime.Duration.System.Runtime.Serialization.ISerializable.GetObjectData (System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext) " ความสามารถในการเข้าถึงความปลอดภัยของวิธีการลบล้างต้องตรงกับความสามารถในการเข้าถึงความปลอดภัยของวิธีการที่ถูกลบล้าง
ฉันได้ จำกัด สิ่งนี้ให้แคบลงเป็นกรอบงานที่กำหนดเป้าหมาย: 1.x เป้าหมาย. NET 3.5 (โปรไฟล์ลูกค้า); 2.x เป้าหมาย. NET 4.5 พวกเขามีความแตกต่างอย่างมากในแง่ของการสนับสนุน PCL เทียบกับ. NET Core และโครงสร้างไฟล์โครงการ แต่ดูเหมือนว่าจะไม่เกี่ยวข้อง
ฉันสามารถทำซ้ำสิ่งนี้ในโครงการในพื้นที่ได้ แต่ฉันไม่พบวิธีแก้ปัญหา
ขั้นตอนในการสร้างซ้ำใน VS2017:
- สร้างโซลูชันใหม่
- สร้างแอปพลิเคชันคอนโซล Windows แบบคลาสสิกใหม่ที่กำหนดเป้าหมายเป็น. NET 4.5.1 ฉันเรียกมันว่า "CodeRunner"
- ในคุณสมบัติโปรเจ็กต์ไปที่การเซ็นชื่อและเซ็นชื่อแอสเซมบลีด้วยคีย์ใหม่ ยกเลิกข้อกำหนดรหัสผ่านและใช้ชื่อไฟล์คีย์ใดก็ได้
Program.cs
วางรหัสต่อไปนี้แทน นี่คือโค้ดเวอร์ชันย่อในตัวอย่าง Microsoftนี้ ฉันได้กำหนดเส้นทางทั้งหมดไว้เหมือนเดิมดังนั้นหากคุณต้องการกลับไปใช้โค้ดที่สมบูรณ์กว่านี้คุณไม่จำเป็นต้องเปลี่ยนอะไรอีก
รหัส:
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" นี่ควรเป็นโปรเจ็กต์ Classic Desktop Class Library
- ลงนามการชุมนุม คุณสามารถใช้คีย์ใหม่หรือคีย์เดียวกับ CodeRunner (นี่เป็นบางส่วนเพื่อเลียนแบบสถานการณ์ Noda Time และอีกส่วนหนึ่งเพื่อให้การวิเคราะห์โค้ดมีความสุข)
- วางรหัสต่อไปนี้ใน
Class1.cs
(เขียนทับสิ่งที่มี):
รหัส:
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 ให้ข้อยกเว้นต่อไปนี้ (จัดรูปแบบใหม่เพื่อให้อ่านง่าย):
Unhandled Exception: System.Reflection.TargetInvocationException:
Exception ถูกโยนโดยเป้าหมายของการเรียกใช้
--->
System.TypeLoadException:
กฎความปลอดภัยในการสืบทอดถูกละเมิดในขณะที่แทนที่สมาชิก:
'UntrustedCode.CustomStruct.System.Runtime.Serialization.ISerializable.GetObjectData (... )
ความสามารถในการเข้าถึงความปลอดภัยของวิธีการลบล้างต้องตรงกับความ
สามารถในการเข้าถึงความปลอดภัยของวิธีการที่ถูกลบล้าง
แอตทริบิวต์ที่แสดงความคิดเห็นแสดงสิ่งที่ฉันได้ลอง:
SecurityPermission
แนะนำโดยสองบทความ MS ที่แตกต่างกัน ( ครั้งแรก , ครั้งที่สอง ) แม้ว่าที่น่าสนใจที่พวกเขาทำสิ่งที่แตกต่างอย่างชัดเจนรอบใช้อินเตอร์เฟซ / ปริยายSecurityCritical
คือสิ่งที่ Noda Time มีอยู่ในปัจจุบันและเป็นสิ่งที่คำตอบของคำถามนี้แนะนำSecuritySafeCritical
ค่อนข้างแนะนำโดยข้อความกฎการวิเคราะห์โค้ด- โดยไม่ต้องใด ๆแอตทริบิวต์กฎการวิเคราะห์รหัสมีความสุข - ด้วย
SecurityPermission
หรือSecurityCritical
ปัจจุบันกฎบอกให้คุณลบแอตทริบิวต์ - ถ้าคุณทำAllowPartiallyTrustedCallers
มี การทำตามคำแนะนำในทั้งสองกรณีไม่ช่วยอะไร - Noda Time
AllowPartiallyTrustedCallers
ใช้กับมันแล้ว ตัวอย่างที่นี่ใช้ไม่ได้ทั้งโดยมีหรือไม่มีแอตทริบิวต์ที่ใช้
รหัสจะทำงานโดยไม่มีข้อยกเว้นหากฉันเพิ่มลง[assembly: SecurityRules(SecurityRuleSet.Level1)]
ในUntrustedCode
แอสเซมบลี (และไม่ใส่เครื่องหมายกำกับAllowPartiallyTrustedCallers
แอตทริบิวต์) แต่ฉันเชื่อว่านั่นเป็นวิธีแก้ปัญหาที่ไม่ดีสำหรับปัญหาที่อาจขัดขวางโค้ดอื่น ๆ
ฉันยอมรับอย่างเต็มที่ว่าหายไปเมื่อพูดถึงความปลอดภัยประเภทนี้ของ. NET ดังนั้นสิ่งที่สามารถฉันทำเพื่อกำหนดเป้าหมาย .NET 4.5 และยังอนุญาตให้มีชนิดของฉันในการดำเนินการISerializable
และยังคงถูกนำมาใช้ในสภาพแวดล้อมเช่น .NET ซอ?
(ในขณะที่ฉันกำหนดเป้าหมาย. NET 4.5 ฉันเชื่อว่าเป็นการเปลี่ยนแปลงนโยบายความปลอดภัย. NET 4.0 ที่ทำให้เกิดปัญหาด้วยเหตุนี้แท็ก)
AllowPartiallyTrustedCallers
ควรทำตามเคล็ดลับ แต่ดูเหมือนจะไม่สร้างความแตกต่าง