Предпосылки: Noda Time содержит много сериализуемых структур. В то время как мне не нравится бинарная сериализация, мы получили много запросов на ее поддержку, вернувшись к временной шкале 1.x. Мы поддерживаем его, реализуя интерфейс ISerializable
.
Мы получили недавний отчет о выпуске Noda Time 2.x в.NET Sciddle. Тот же код с использованием Noda Time 1.x отлично работает. Исключение составляет следующее:
Правила безопасности наследования нарушены при переопределении элемента: 'NodaTime.Duration.System.Runtime.Serialization.ISerializable.GetObjectData(System.Runtime.Serialization.SerializationInfo, System.Runtime.Serialization.StreamingContext)'. Доступность безопасности для переопределяющего метода должна соответствовать доступности безопасности для переопределяемого метода.
Я сузил это до рамки, на которую нацелены: 1.x target.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". Это должен быть проект библиотеки Class Class Class.
- Подпишите сборку; вы можете использовать новый ключ или тот же, что и для 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
несколько предлагает сообщения правила анализа кода - Без каких-либо атрибутов правила Code Analysis довольны - либо с помощью
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 вызвали эту проблему, следовательно, тег.)