Contexte: Noda Time contient de nombreuses structures sérialisables. Bien que je n'aime pas la sérialisation binaire, nous avons reçu de nombreuses demandes pour la prendre en charge, dans la chronologie 1.x. Nous le soutenons en implémentant l' ISerializable
interface.
Nous avons reçu un rapport récent sur l' échec de Noda Time 2.x dans .NET Fiddle . Le même code utilisant Noda Time 1.x fonctionne correctement. L'exception levée est la suivante:
Règles de sécurité d'héritage violées lors du remplacement du membre: «NodaTime.Duration.System.Runtime.Serialization.ISerializable.GetObjectData (System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)». L'accessibilité de sécurité de la méthode de substitution doit correspondre à l'accessibilité de sécurité de la méthode remplacée.
J'ai réduit cela au cadre ciblé: 1.x cibles .NET 3.5 (profil client); 2.x cible .NET 4.5. Ils présentent de grandes différences en termes de prise en charge de PCL par rapport à .NET Core et de la structure des fichiers de projet, mais il semble que cela ne soit pas pertinent.
J'ai réussi à reproduire cela dans un projet local, mais je n'ai pas trouvé de solution.
Étapes à suivre pour reproduire dans VS2017:
- Créer une nouvelle solution
- Créez une nouvelle application console Windows classique ciblant .NET 4.5.1. Je l'ai appelé "CodeRunner".
- Dans les propriétés du projet, accédez à Signature et signez l'assembly avec une nouvelle clé. Décochez l'exigence de mot de passe et utilisez n'importe quel nom de fichier de clé.
- Collez le code suivant pour remplacer
Program.cs
. Il s'agit d'une version abrégée du code de cet exemple Microsoft . J'ai gardé tous les chemins identiques, donc si vous voulez revenir au code plus complet, vous ne devriez rien changer d'autre.
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);
}
}
- Créez un autre projet appelé "UntrustedCode". Il doit s'agir d'un projet de bibliothèque de classes de bureau classique.
- Signez l'assemblée; vous pouvez utiliser une nouvelle clé ou la même que pour CodeRunner. (Ceci est en partie pour imiter la situation Noda Time, et en partie pour garder l'analyse de code heureuse.)
- Collez le code suivant dans
Class1.cs
(en écrasant ce qu'il y a):
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();
}
}
}
L'exécution du projet CodeRunner donne l'exception suivante (reformatée pour plus de lisibilité):
Exception non gérée: System.Reflection.TargetInvocationException: une
exception a été levée par la cible d'un appel.
--->
System.TypeLoadException:
règles de sécurité d'héritage violées lors
du remplacement du membre: 'UntrustedCode.CustomStruct.System.Runtime.Serialization.ISerializable.GetObjectData (...).
L'accessibilité de sécurité
de la méthode de substitution doit correspondre à l' accessibilité de sécurité de la méthode remplacée.
Les attributs commentés montrent des choses que j'ai essayées:
SecurityPermission
est recommandé par deux articles MS différents ( premier , deuxième ), bien qu'il soit intéressant de noter qu'ils font des choses différentes autour de l'implémentation d'interface explicite / impliciteSecurityCritical
est ce que Noda Time a actuellement, et c'est ce que la réponse à cette question suggèreSecuritySafeCritical
est quelque peu suggéré par les messages de règle d'analyse du code- Sans aucun attributs, code des règles d' analyse sont heureux - soit avec
SecurityPermission
ouSecurityCritical
présent, les règles que vous disent de supprimer les attributs - à moins que vous n'avez . Suivre les suggestions dans les deux cas n'aide pas.AllowPartiallyTrustedCallers
- Noda Time s'y est
AllowPartiallyTrustedCallers
appliqué; l'exemple ici ne fonctionne pas avec ou sans l'attribut appliqué.
Le code fonctionne sans exception si j'ajoute [assembly: SecurityRules(SecurityRuleSet.Level1)]
à l' UntrustedCode
assembly (et décommente l' AllowPartiallyTrustedCallers
attribut), mais je pense que c'est une mauvaise solution au problème qui pourrait gêner d'autres codes.
J'avoue pleinement être assez perdu en ce qui concerne ce genre d'aspect de sécurité de .NET. Alors, que puis- je faire pour cibler .NET 4.5 tout en autorisant mes types à implémenter ISerializable
et à être encore utilisés dans des environnements tels que .NET Fiddle?
(Bien que je cible .NET 4.5, je pense que ce sont les changements de politique de sécurité .NET 4.0 qui ont causé le problème, d'où la balise.)
AllowPartiallyTrustedCallers
devrait faire l'affaire, mais cela ne semble pas faire de différence