背景: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で再現する手順:
- 新しいソリューションを作成する
- .NET 4.5.1をターゲットとする新しいクラシックWindowsコンソールアプリケーションを作成します。私はそれを「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と同じキーを使用できます。(これは、一部は野田時間の状況を模倣するためであり、一部はコード分析を幸せに保つためです。)
- 次のコードを
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
2つの異なるMS記事(first、 second)で推奨されていますが、興味深いことに、明示的/暗黙的なインターフェース実装に関して異なることをしています。SecurityCritical
野田タイムが現在持っているものであり、この質問の答えが示唆するものですSecuritySafeCritical
コード分析ルールのメッセージでやや示唆されている- せずに任意の属性は、コード分析ルールは満足している-のいずれかで
SecurityPermission
、またはSecurityCritical
あなたがない限り-現在、ルールは、属性を削除するためにあなたを教えてください持っていますAllowPartiallyTrustedCallers
。どちらの場合も提案に従うことは役に立ちません。 - 野田タイムがそれに
AllowPartiallyTrustedCallers
適用されました。この例は、属性を適用してもしなくても機能しません。
アセンブリに追加[assembly: SecurityRules(SecurityRuleSet.Level1)]
した場合UntrustedCode
(およびAllowPartiallyTrustedCallers
属性のコメントを解除した場合)、コードは例外なく実行されますが、他のコードを妨げる可能性がある問題の解決策としては不十分だと思います。
.NETのこの種のセキュリティの側面に関しては、私は完全に迷っていることを完全に認めています。では、.NET 4.5をターゲットにして、タイプを実装し、.NET Fiddleなどの環境で引き続き使用できるようにするにはどうISerializable
すればよいですか?
(.NET 4.5をターゲットにしていますが、問題の原因となったのは.NET 4.0セキュリティポリシーの変更であり、したがってタグであると考えています。)
AllowPartiallyTrustedCallers
でうまくいくことを示唆していますが、違いはないようです