Background: Noda Time contiene molte strutture serializzabili. Anche se non mi piace la serializzazione binaria, abbiamo ricevuto molte richieste per supportarla, nella timeline 1.x. Lo supportiamo implementando l' ISerializable
interfaccia.
Abbiamo ricevuto un recente rapporto sul problema di Noda Time 2.x che non funziona in .NET Fiddle . Lo stesso codice che utilizza Noda Time 1.x funziona bene. L'eccezione generata è questa:
Regole di sicurezza sull'ereditarietà violate durante l'override del membro: 'NodaTime.Duration.System.Runtime.Serialization.ISerializable.GetObjectData (System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)'. L'accessibilità di sicurezza del metodo di sovrascrittura deve corrispondere all'accessibilità di sicurezza del metodo sottoposto a override.
L'ho ristretto al framework mirato: 1.x è destinato a .NET 3.5 (profilo client); 2.x è destinato a .NET 4.5. Hanno grandi differenze in termini di supporto PCL vs .NET Core e la struttura del file di progetto, ma sembra che questo sia irrilevante.
Sono riuscito a riprodurlo in un progetto locale, ma non ho trovato una soluzione.
Passaggi per riprodurre in VS2017:
- Crea una nuova soluzione
- Crea una nuova applicazione console Windows classica destinata a .NET 4.5.1. L'ho chiamato "CodeRunner".
- Nelle proprietà del progetto passare a Firma e firmare l'assembly con una nuova chiave. Deselezionare il requisito della password e utilizzare un nome file di chiavi qualsiasi.
- Incolla il codice seguente da sostituire
Program.cs
. Questa è una versione abbreviata del codice in questo esempio Microsoft . Ho mantenuto tutti i percorsi uguali, quindi se vuoi tornare al codice completo, non dovresti aver bisogno di cambiare nient'altro.
Codice:
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);
}
}
- Crea un altro progetto chiamato "UntrustedCode". Dovrebbe essere un progetto di libreria di classi desktop classico.
- Firmare l'assemblea; puoi usare una nuova chiave o la stessa di CodeRunner. (Questo è in parte per imitare la situazione Noda Time e in parte per mantenere felice l'analisi del codice.)
- Incolla il seguente codice
Class1.cs
(sovrascrivendo quello che c'è):
Codice:
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();
}
}
}
L'esecuzione del progetto CodeRunner genera la seguente eccezione (riformattata per la leggibilità):
Eccezione non gestita: System.Reflection.TargetInvocationException:
eccezione generata dalla destinazione di una chiamata.
--->
System.TypeLoadException:
regole di sicurezza dell'ereditarietà violate durante l'override del membro:
'UntrustedCode.CustomStruct.System.Runtime.Serialization.ISerializable.GetObjectData (...).
L'accessibilità di sicurezza del metodo di sovrascrittura deve corrispondere
all'accessibilità di sicurezza del metodo sottoposto a override.
Gli attributi commentati mostrano le cose che ho provato:
SecurityPermission
è raccomandato da due diversi articoli di MS ( primo , secondo ), anche se è interessante notare che fanno cose diverse riguardo all'implementazione dell'interfaccia esplicita / implicitaSecurityCritical
è ciò che Noda Time ha attualmente, ed è ciò che suggerisce la risposta di questa domandaSecuritySafeCritical
è in qualche modo suggerito dai messaggi delle regole di analisi del codice- Senza alcun attributo, le regole di analisi del codice sono soddisfatte - con uno
SecurityPermission
o piùSecurityCritical
presenti, le regole ti dicono di rimuovere gli attributi - a meno che tu non lo abbiaAllowPartiallyTrustedCallers
. Seguire i suggerimenti in entrambi i casi non aiuta. - Noda Time ha
AllowPartiallyTrustedCallers
applicato ad esso; l'esempio qui non funziona né con né senza l'attributo applicato.
Le piste codice senza un'eccezione se aggiungo [assembly: SecurityRules(SecurityRuleSet.Level1)]
al UntrustedCode
montaggio (e rimuovere il commento dalla AllowPartiallyTrustedCallers
attributi), ma credo che sia una cattiva soluzione al problema che potrebbe ostacolare altro codice.
Ammetto pienamente di essere abbastanza perso quando si tratta di questo tipo di aspetto della sicurezza di .NET. Quindi cosa posso fare per scegliere come target .NET 4.5 e consentire tuttavia ai miei tipi di implementare ISerializable
ed essere ancora utilizzati in ambienti come .NET Fiddle?
(Anche se sto prendendo di mira .NET 4.5, credo che siano le modifiche alla politica di sicurezza di .NET 4.0 che hanno causato il problema, da cui il tag.)
AllowPartiallyTrustedCallers
dovrebbe fare il trucco, ma non sembra fare la differenza