Hintergrund: Noda Time enthält viele serialisierbare Strukturen. Obwohl ich die binäre Serialisierung nicht mag, haben wir viele Anfragen erhalten, sie zu unterstützen, zurück in der 1.x-Timeline. Wir unterstützen es durch die Implementierung der ISerializable
Schnittstelle.
Wir haben kürzlich einen Problembericht erhalten, in dem Noda Time 2.x in .NET Fiddle fehlschlägt . Der gleiche Code mit Noda Time 1.x funktioniert einwandfrei. Die Ausnahme ist folgende:
Vererbungssicherheitsregeln beim Überschreiben des Mitglieds verletzt: 'NodaTime.Duration.System.Runtime.Serialization.ISerializable.GetObjectData (System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)'. Die Sicherheitszugänglichkeit der überschreibenden Methode muss mit der Sicherheitszugänglichkeit der überschriebenen Methode übereinstimmen.
Ich habe dies auf das Framework eingegrenzt, auf das abgezielt wird: 1.x zielt auf .NET 3.5 (Client-Profil) ab; 2.x zielt auf .NET 4.5 ab. Sie haben große Unterschiede in Bezug auf die Unterstützung von PCL gegenüber .NET Core und die Struktur der Projektdatei, aber es sieht so aus, als ob dies irrelevant ist.
Ich habe es geschafft, dies in einem lokalen Projekt zu reproduzieren, aber ich habe keine Lösung dafür gefunden.
Schritte zum Reproduzieren in VS2017:
- Erstellen Sie eine neue Lösung
- Erstellen Sie eine neue klassische Windows-Konsolenanwendung für .NET 4.5.1. Ich habe es "CodeRunner" genannt.
- Gehen Sie in den Projekteigenschaften zu Signieren und signieren Sie die Assembly mit einem neuen Schlüssel. Deaktivieren Sie die Kennwortanforderung und verwenden Sie einen beliebigen Schlüsseldateinamen.
- Fügen Sie den folgenden Code zum Ersetzen ein
Program.cs
. Dies ist eine abgekürzte Version des Codes in diesem Microsoft-Beispiel . Ich habe alle Pfade gleich gehalten. Wenn Sie also zum vollständigeren Code zurückkehren möchten, sollten Sie nichts anderes ändern müssen.
Code:
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);
}
}
- Erstellen Sie ein weiteres Projekt mit dem Namen "UntrustedCode". Dies sollte ein klassisches Desktop Class Library-Projekt sein.
- Unterschreiben Sie die Versammlung; Sie können einen neuen oder denselben Schlüssel wie für CodeRunner verwenden. (Dies dient zum Teil dazu, die Noda Time-Situation nachzuahmen, und zum Teil, um die Code-Analyse bei Laune zu halten.)
- Fügen Sie den folgenden Code ein
Class1.cs
(überschreiben Sie, was vorhanden ist):
Code:
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();
}
}
}
Das Ausführen des CodeRunner-Projekts führt zu der folgenden Ausnahme (aus Gründen der Lesbarkeit neu formatiert):
Nicht behandelte Ausnahme: System.Reflection.TargetInvocationException: Die
Ausnahme wurde vom Ziel eines Aufrufs ausgelöst.
--->
System.TypeLoadException:
Vererbungssicherheitsregeln beim Überschreiben des Mitglieds verletzt:
'UntrustedCode.CustomStruct.System.Runtime.Serialization.ISerializable.GetObjectData (...).
Die Sicherheitszugänglichkeit der überschreibenden Methode muss mit der Sicherheitszugänglichkeit der überschriebenen Methode übereinstimmen
.
Die auskommentierten Attribute zeigen Dinge, die ich versucht habe:
SecurityPermission
wird von zwei verschiedenen MS-Artikeln empfohlen ( erster , zweiter ), obwohl sie interessanterweise verschiedene Dinge in Bezug auf die explizite / implizite Schnittstellenimplementierung tunSecurityCritical
ist das, was Noda Time derzeit hat und was die Antwort dieser Frage nahelegtSecuritySafeCritical
wird durch Code Analysis-Regelmeldungen etwas vorgeschlagen- Ohne irgendwelche Attribute sind Code - Analyse - Regeln glücklich - entweder mit
SecurityPermission
oderSecurityCritical
Gegenwart, die Regeln , die Sie sagen , die Attribute entfernen - es sei denn , Sie tun habenAllowPartiallyTrustedCallers
. In beiden Fällen hilft es nicht, den Vorschlägen zu folgen. - Noda Time hat sich darauf
AllowPartiallyTrustedCallers
beworben; Das Beispiel hier funktioniert weder mit noch ohne das angewendete Attribut.
Der Code wird ausnahmslos ausgeführt, wenn ich [assembly: SecurityRules(SecurityRuleSet.Level1)]
der UntrustedCode
Assembly etwas hinzufüge (und das AllowPartiallyTrustedCallers
Attribut auskommentiere ), aber ich glaube, dass dies eine schlechte Lösung für das Problem ist, das anderen Code behindern könnte.
Ich gebe voll und ganz zu, dass ich ziemlich verloren bin, wenn es um diese Art von Sicherheitsaspekt von .NET geht. Was kann ich also tun, um auf .NET 4.5 abzuzielen und dennoch zuzulassen, dass meine Typen ISerializable
in Umgebungen wie .NET Fiddle implementiert werden und weiterhin verwendet werden?
(Während ich auf .NET 4.5 abziele, sind es meiner Meinung nach die Änderungen der .NET 4.0-Sicherheitsrichtlinien, die das Problem verursacht haben, daher das Tag.)
AllowPartiallyTrustedCallers
den Trick machen sollte, aber es scheint keinen Unterschied zu machen