Предпосылки: Noda Time содержит множество сериализуемых структур. Хотя мне не нравится двоичная сериализация, мы получили много запросов на ее поддержку еще на временной шкале 1.x. Мы поддерживаем это реализацией ISerializable
интерфейса.
Мы получили недавний отчет о сбое Noda Time 2.x в .NET Fiddle . Тот же код, использующий 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». Это должен быть проект библиотеки классов классического рабочего стола.
- Подпишите сборку; вы можете использовать новый ключ или тот же, что и для 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 возникает следующее исключение (переформатировано для удобства чтения):
Необработанное исключение: System.Reflection.TargetInvocationException:
исключение было выброшено целью вызова.
--->
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 Fiddle?
(Хотя я нацелен на .NET 4.5, я считаю, что проблема была вызвана изменениями политики безопасности .NET 4.0, отсюда и тег.)
AllowPartiallyTrustedCallers
должно помочь, но, похоже, это не имеет значения,