Как я могу реализовать ISerializable in.NET 4+ без нарушения правил безопасности наследования?
Справочная информация: Нода Времени содержит много
сериализуемые структуры. Хотя мне не нравится двоичная сериализация, мы
получил много запросов, чтобы поддержать его, еще в 1.х сроков.
Мы поддерживаем его, реализуя ISerializable
интерфейс.
мы получили недавно вопрос доклад Нода Время 2.x не в пределах .Чистая Скрипка. Один и тот же код с помощью Нода Время 1.X работает нормально. Исключение создается следующим образом:
наследование нарушены правила безопасности при переопределении член: - Нодатим.Продолжительность.Система.Во время выполнения.Сериализация.Интерфейс ISerializable.GetObjectData (System.Во время выполнения.Сериализация.SerializationInfo, Система.Во время выполнения.Сериализация.StreamingContext)'. Безопасность доступность переопределяющего метода должна соответствовать безопасности доступность переопределяемого метода.
я сузил это до рамок, которые предназначены: 1.икс целевые объекты .NET 3.5 (профиль клиента); 2.икс цели .NET 4.5. У них есть большие различия с точки зрения поддержки PCL vs .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(@"......UntrustedCodebinDebug");
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, и отчасти, чтобы сохранить анализ кода счастливым.)
- вставьте следующий код в
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 дает следующее исключение (переформатировано для удобства чтения):
Необработанное Исключение: Система.Отображение.TargetInvocationException:
Исключение вызвано адресат вызова.
--- >
Система.TypeLoadException:
Наследование нарушены правила безопасности при переопределении член:
- Недоверенный код.CustomStruct.Система.Во время выполнения.Сериализация.Интерфейс ISerializable.GetObjectData(...).
Доступность безопасности переопределяющего метода должна соответствовать безопасность
доступность переопределяемого метода.
закомментированные атрибуты показывают вещи, которые я пробовал:
-
SecurityPermission
рекомендуется двумя различными статьями MS (первый, второй), хотя интересно, что они делают разные вещи вокруг явной / неявной реализации интерфейса -
SecurityCritical
это то, что нода время в настоящее время имеет, и это то, что ответ на этот вопрос предлагает -
SecuritySafeCritical
несколько подсказывает правило анализа кода messages - без любой атрибуты, правила анализа кода довольны - с
SecurityPermission
илиSecurityCritical
представьте, правила говорят вам удалить атрибуты-если вы do естьAllowPartiallyTrustedCallers
. Следующие предложения в любом случае не поможет. - Нода времени
AllowPartiallyTrustedCallers
применяется к нему; пример здесь не работает ни с атрибутом, ни без него прикладная.
код работает без исключения, если я добавлю [assembly: SecurityRules(SecurityRuleSet.Level1)]
до UntrustedCode
сборка (и раскомментируйте AllowPartiallyTrustedCallers
атрибут), но я считаю, что это плохое решение проблемы, которая может помешать другому коду.
я полностью признаю, что довольно потерян, когда дело доходит до такого рода
аспект безопасности .NET. So what можете я делаю для целевой .NET 4.5 и
но позвольте моим типам реализовать ISerializable
и все еще используйте внутри
такие среды, как .NET Скрипка?
(в то время как я нацелен на .NET 4.5, я считаю, что это изменения политики безопасности .NET 4.0, которые вызвали проблему, следовательно, тег.)
2 ответа:
по данным в разделе, в .NET 4.0 в основном вы не должны использовать
ISerializable
для частично доверенного кода, и вместо этого вы должны использовать ISafeSerializationDataцитирование из https://docs.microsoft.com/en-us/dotnet/standard/serialization/custom-serialization
важно
в версиях, предшествующих .NET Framework 4.0, сериализация пользовательских данных в частично доверенном сборка была выполнена с использованием GetObjectData. Начиная с версии 4.0, этот метод помечается атрибутом SecurityCriticalAttribute, который предотвращает выполнение в сборках с частичным доверием. Чтобы обойти это условие, реализуйте интерфейс ISafeSerializationData.
так что, вероятно, не то, что вы хотели услышать, если вам это нужно, но я не думаю, что есть какой-либо способ обойти это, продолжая использовать
ISerializable
(кроме возвращения кLevel1
безопасность, которую вы сказал, что не хочешь).ЗЫ:
ISafeSerializationData
документы утверждают, что это только для исключений, но это не кажется все, что конкретно, вы можете дать ему шанс... Я в принципе не могу проверить его с вашим примером кода (кроме удаленияISerializable
работает, но вы это уже знали)... вы должны будете увидеть, еслиISafeSerializationData
достаточно подходит вам.PS2: the
SecurityCritical
атрибут не работает, потому что он игнорируется при загрузке сборки в режиме частичного доверия ( на уровне 2 безопасность). Вы можете увидеть это в своем примере кода, если вы отлаживаетеtarget
переменнаяExecuteUntrustedCode
прямо перед вызовом он будет иметьIsSecurityTransparent
доtrue
иIsSecurityCritical
доfalse
даже если вы отмечаете метод с )
по словам MSDN посмотреть:
как исправить нарушения?
чтобы исправить нарушение этого правила, Сделайте GetObjectData метод видимый и переопределяемый и убедитесь, что все поля экземпляра включены в процесс сериализации или явно помечены атрибутом nonserializedattribute.
следующее пример исправляет два предыдущих нарушения путем обеспечение переопределяемой реализации ISerializable.GetObjectData в классе Book и путем предоставления реализации ISerializable.GetObjectData в классе библиотеки.
using System; using System.Security.Permissions; using System.Runtime.Serialization; namespace Samples2 { [Serializable] public class Book : ISerializable { private readonly string _Title; public Book(string title) { if (title == null) throw new ArgumentNullException("title"); _Title = title; } protected Book(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException("info"); _Title = info.GetString("Title"); } public string Title { get { return _Title; } } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] protected virtual void GetObjectData(SerializationInfo info, StreamingContext context) { info.AddValue("Title", _Title); } [SecurityPermission(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.SerializationFormatter)] void ISerializable.GetObjectData(SerializationInfo info, StreamingContext context) { if (info == null) throw new ArgumentNullException("info"); GetObjectData(info, context); } } [Serializable] public class LibraryBook : Book { private readonly DateTime _CheckedOut; public LibraryBook(string title, DateTime checkedOut) : base(title) { _CheckedOut = checkedOut; } protected LibraryBook(SerializationInfo info, StreamingContext context) : base(info, context) { _CheckedOut = info.GetDateTime("CheckedOut"); } public DateTime CheckedOut { get { return _CheckedOut; } } [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] protected override void GetObjectData(SerializationInfo info, StreamingContext context) { base.GetObjectData(info, context); info.AddValue("CheckedOut", _CheckedOut); } } }