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.)
quelle
AllowPartiallyTrustedCallers
den Trick machen sollte, aber es scheint keinen Unterschied zu machenAntworten:
Laut MSDN sollten Sie in .NET 4.0 grundsätzlich keinen
ISerializable
teilweise vertrauenswürdigen Code verwenden, sondern ISafeSerializationDataZitieren von https://docs.microsoft.com/en-us/dotnet/standard/serialization/custom-serialization
Also wahrscheinlich nicht das, was Sie hören wollten, wenn Sie es brauchen, aber ich glaube, es gibt keinen Weg daran vorbei, während Sie es weiter verwenden
ISerializable
(außer zurLevel1
Sicherheit zurückzukehren, von der Sie sagten, dass Sie es nicht wollen).PS: In den
ISafeSerializationData
Dokumenten wird angegeben, dass es sich nur um Ausnahmen handelt, aber es scheint nicht so spezifisch zu sein. Vielleicht möchten Sie es ausprobieren ... Ich kann es im Grunde nicht mit Ihrem Beispielcode testen (außer das Entfernen vonISerializable
Werken, aber das wusstest du schon) ... du musst sehen, ob esISafeSerializationData
dir genug passt.PS2: Das
SecurityCritical
Attribut funktioniert nicht, da es ignoriert wird, wenn die Assembly im Teilvertrauensmodus geladen wird ( auf Level2-Sicherheit ). Sie können es auf Ihrem Beispielcode sehen, wenn Sie den Debug -target
Variable in derExecuteUntrustedCode
rechten , bevor es aufgerufen wird , wird es hatIsSecurityTransparent
auftrue
undIsSecurityCritical
zu ,false
auch wenn Sie die Methode mit dem markierenSecurityCritical
Attribute)quelle
Die akzeptierte Antwort ist so überzeugend, dass ich fast geglaubt hätte, dies sei kein Fehler. Aber nachdem ich jetzt einige Experimente durchgeführt habe, kann ich sagen, dass Level2-Sicherheit ein komplettes Durcheinander ist. Zumindest ist etwas wirklich faul.
Vor ein paar Tagen bin ich mit meinen Bibliotheken auf dasselbe Problem gestoßen. Ich habe schnell einen Unit-Test erstellt. Ich konnte jedoch das in .NET Fiddle aufgetretene Problem nicht reproduzieren, während derselbe Code die Ausnahme in einer Konsolen-App "erfolgreich" auslöste. Am Ende habe ich zwei seltsame Wege gefunden, um das Problem zu lösen.
TL; DR : Wenn Sie in Ihrem Consumer-Projekt einen internen Typ der verwendeten Bibliothek verwenden, funktioniert der teilweise vertrauenswürdige Code wie erwartet: Er kann eine
ISerializable
Implementierung instanziieren (und ein sicherheitskritischer Code kann nicht direkt aufgerufen werden.) aber siehe unten). Oder, was noch lächerlicher ist, Sie können versuchen, die Sandbox erneut zu erstellen, wenn sie zum ersten Mal nicht funktioniert hat ...Aber sehen wir uns einen Code an.
ClassLibrary.dll:
Lassen Sie uns zwei Fälle trennen: einen für eine reguläre Klasse mit sicherheitskritischen Inhalten und einen für die
ISerializable
Implementierung:Eine Möglichkeit, das Problem zu beheben, besteht darin, einen internen Typ aus der Consumer-Assembly zu verwenden. Jeder Typ wird es tun; Jetzt definiere ich ein Attribut:
Und die relevanten Attribute, die auf die Baugruppe angewendet werden:
Signieren Sie die Assembly, wenden Sie den Schlüssel auf das
InternalsVisibleTo
Attribut an und bereiten Sie sich auf das Testprojekt vor:UnitTest.dll (verwendet NUnit und ClassLibrary):
Um den internen Trick anzuwenden, sollte auch die Testbaugruppe signiert werden. Baugruppenattribute:
Hinweis : Das Attribut kann überall angewendet werden. In meinem Fall war es eine Methode in einer zufälligen Testklasse, für die ich ein paar Tage gebraucht habe.
Hinweis 2 : Wenn Sie alle Testmethoden zusammen ausführen, kann es vorkommen, dass die Tests bestanden werden.
Das Skelett der Testklasse:
Und schauen wir uns die Testfälle einzeln an
Fall 1: ISerialisierbare Implementierung
Das gleiche Problem wie in der Frage. Der Test besteht wenn
InternalTypeReferenceAttribute
wird angewandtAndernfalls tritt
Inheritance security rules violated while overriding member...
beim Instanziieren die völlig unangemessene Ausnahme aufSerializableCriticalClass
.Fall 2: Regelmäßiger Unterricht mit sicherheitskritischen Mitgliedern
Der Test besteht unter den gleichen Bedingungen wie der erste. Das Problem ist hier jedoch völlig anders: Ein teilweise vertrauenswürdiger Code kann direkt auf ein sicherheitskritisches Mitglied zugreifen .
Fall 3-4: Vollvertrauensversionen von Fall 1-2
Der Vollständigkeit halber werden hier dieselben Fälle wie oben in einer vollständig vertrauenswürdigen Domäne ausgeführt. Wenn Sie
[assembly: AllowPartiallyTrustedCallers]
die fehlgeschlagenen Tests entfernen , können Sie direkt auf kritischen Code zugreifen (da die Methoden standardmäßig nicht mehr transparent sind).Epilog:
Dies wird Ihr Problem mit .NET Fiddle natürlich nicht lösen. Aber jetzt wäre ich sehr überrascht, wenn es kein Fehler im Framework wäre.
Die größte Frage für mich ist jetzt der zitierte Teil in der akzeptierten Antwort. Wie sind sie mit diesem Unsinn herausgekommen? Das
ISafeSerializationData
ist eindeutig keine Lösung für irgendetwas: Es wird ausschließlich von der Basisklasse verwendetException
und wenn Sie dasSerializeObjectState
Ereignis abonnieren (warum ist das keine überschreibbare Methode?), Wird der Status am Ende auch von der konsumiertException.GetObjectData
.Das
AllowPartiallyTrustedCallers
/SecurityCritical
/SecuritySafeCritical
Triumvirat der Attribute wurde genau für die oben gezeigte Verwendung entwickelt. Es scheint mir totaler Unsinn, dass ein teilweise vertrauenswürdiger Code einen Typ nicht einmal instanziieren kann, unabhängig davon, ob versucht wird, seine sicherheitskritischen Mitglieder zu verwenden. Es ist jedoch ein noch größerer Unsinn ( eigentlich eine Sicherheitslücke ), dass ein teilweise vertrauenswürdiger Code direkt auf eine sicherheitskritische Methode zugreifen kann (siehe Fall 2 ), während dies für transparente Methoden selbst aus einer vollständig vertrauenswürdigen Domäne verboten ist.Wenn es sich bei Ihrem Verbraucherprojekt also um einen Test oder eine andere bekannte Baugruppe handelt, kann der interne Trick perfekt angewendet werden. Für .NET Fiddle und andere reale Sandbox-Umgebungen ist die einzige Lösung, auf die zurückgesetzt wird,
SecurityRuleSet.Level1
bis dies von Microsoft behoben wurde.Update: Für das Problem wurde ein Developer Community-Ticket erstellt.
quelle
Laut MSDN siehe:
quelle
GetObjectData
explizit implementiert , aber dies hilft implizit nicht.