次の方法で共有


BinaryFormatter クリップボードのサポートを有効にする (推奨されません)

注意事項

BinaryFormatter サポートは推奨されません。 これは、新しいタイプ セーフな API にすぐに移行できないレガシ アプリケーションの一時的な移行ブリッジとしてのみ使用します。 このアプローチでは、重大なセキュリティ リスクが伴います。

この記事では、.NET 10 で Windows フォーム クリップボード操作の制限付き BinaryFormatter サポートを構成する方法について説明します。 セキュリティの脆弱性により、 BinaryFormatter は .NET 9 のランタイムから削除されましたが、移行に時間が必要なレガシ アプリケーションの明示的な構成によって制限された機能を復元します。

新しいタイプ セーフ API への完全な移行ガイダンスについては、 .NET 10 での Windows フォーム クリップボードと DataObject の変更に関する説明を参照してください。

Important

このコンテンツは、特に指定がない限り、最新の .NET にのみ適用され、.NET Framework には適用されません

[前提条件]

続行する前に、次の概念を確認してください。

  • 現在、アプリケーションがクリップボード操作で BinaryFormatter を使用する方法。
  • BinaryFormatterの削除につながったセキュリティの脆弱性。
  • 新しいタイプセーフクリップボード API への移行タイムライン。

詳細については、次の記事を参照してください。

セキュリティの警告とリスク

BinaryFormatter は本質的に安全でなくなり、次の理由で非推奨になります。

  • 任意のコード実行の脆弱性: 攻撃者は逆シリアル化中に悪意のあるコードを実行し、アプリケーションをリモート攻撃にさらす可能性があります。
  • サービス拒否攻撃: 悪意のあるクリップボード データは、過剰なメモリや CPU リソースを消費し、クラッシュや不安定を引き起こす可能性があります。
  • 情報漏えいのリスク: 攻撃者はメモリから機密データを抽出する可能性があります。
  • セキュリティ境界なし: この形式は基本的に安全ではなく、構成設定ではセキュリティで保護できません。

このサポートは、新しいタイプ セーフ API を使用するようにアプリケーションを更新するときに、一時的なブリッジとしてのみ有効にします。

互換性パッケージをインストールする

サポートされていない BinaryFormatter 互換性パッケージをプロジェクトに追加します。 このパッケージは、 BinaryFormatter 操作に必要なランタイム サポートを提供します。

<ItemGroup>
  <PackageReference Include="System.Runtime.Serialization.Formatters" Version="10.0.0*-*"/>
</ItemGroup>

このパッケージは、サポートされていないと非推奨としてマークされています。 移行中の一時的な互換性のためにのみ使用します。

プロジェクトで安全でないシリアル化を有効にする

EnableUnsafeBinaryFormatterSerialization プロパティをプロジェクト ファイルにtrueするように設定します。 このプロパティは、 BinaryFormatter の使用を許可するようにコンパイラに指示します。

<PropertyGroup>
  <TargetFramework>net10.0</TargetFramework>
  <EnableUnsafeBinaryFormatterSerialization>true</EnableUnsafeBinaryFormatterSerialization>
</PropertyGroup>

この設定がない場合、アプリケーションでは、 BinaryFormatter API を使用しようとしたときにコンパイル エラーが生成されます。

Windows フォーム ランタイム スイッチを構成する

アプリケーションの runtimeconfig.json ファイルを作成または更新して、Windows フォーム固有のクリップボードの切り替えを有効にします。 この構成により、必要に応じてクリップボード操作を BinaryFormatter にフォールバックできます。

{
  "runtimeOptions": {
    "configProperties": {
      "Windows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization": true
    }
  }
}

Important

この特定のランタイム スイッチがないと、一般的なシリアル化のサポートが有効になっている場合でも、クリップボード操作は BinaryFormatter にフォールバックしません。 このスイッチは、Windows フォームと WPF クリップボードの機能に特に必要です。

セキュリティに重点を置いた型リゾルバーを実装する

BinaryFormatterが有効になっている場合でも、型リゾルバーを実装して、逆シリアル化を明示的に承認された型に制限します。 型リゾルバーは、悪意のあるペイロード攻撃に対する唯一の防御を提供します。

セキュリティで保護された型リゾルバーを作成する

// Create a security-focused type resolver
private static Type SecureTypeResolver(TypeName typeName)
{
    // Explicit allow-list of permitted types—add only what you need
    var allowedTypes = new Dictionary<string, Type>
    {
        ["MyApp.Person"] = typeof(Person),
        ["MyApp.AppSettings"] = typeof(AppSettings),
        ["System.String"] = typeof(string),
        ["System.Int32"] = typeof(int),
        // Add only the specific types your application requires
    };

    // Only allow explicitly listed types - exact string match required
    if (allowedTypes.TryGetValue(typeName.FullName, out Type allowedType))
    {
        return allowedType;
    }

    // Reject any type not in the allow-list with clear error message
    throw new InvalidOperationException(
        $"Type '{typeName.FullName}' is not permitted for clipboard deserialization");
}
' Create a security-focused type resolver
Private Shared Function SecureTypeResolver(typeName As TypeName) As Type
    ' Explicit allow-list of permitted types—add only what you need
    Dim allowedTypes As New Dictionary(Of String, Type) From {
        {"MyApp.Person", GetType(Person)},
        {"MyApp.AppSettings", GetType(AppSettings)},
        {"System.String", GetType(String)},
        {"System.Int32", GetType(Integer)}
    } ' Add only the specific types your application requires

    ' Only allow explicitly listed types - exact string match required
    Dim allowedType As Type = Nothing
    If allowedTypes.TryGetValue(typeName.FullName, allowedType) Then
        Return allowedType
    End If

    ' Reject any type not in the allow-list with clear error message
    Throw New InvalidOperationException(
        $"Type '{typeName.FullName}' is not permitted for clipboard deserialization")
End Function

クリップボード操作で型リゾルバーを使用する

// Use the resolver with clipboard operations
private static Type SecureTypeResolver(TypeName typeName)
{
    // Implementation from SecureTypeResolver example
    // ... (allow-list implementation here)
    throw new InvalidOperationException($"Type '{typeName.FullName}' is not permitted");
}

public static void UseSecureTypeResolver()
{
    // Retrieve legacy data using the secure type resolver
    if (Clipboard.TryGetData("LegacyData", SecureTypeResolver, out MyCustomType data))
    {
        ProcessLegacyData(data);
    }
    else
    {
        Console.WriteLine("No compatible data found on clipboard");
    }
}
' Use the resolver with clipboard operations
Private Shared Function SecureTypeResolver(typeName As TypeName) As Type
    ' Implementation from SecureTypeResolver example
    ' ... (allow-list implementation here)
    Throw New InvalidOperationException($"Type '{typeName.FullName}' is not permitted")
End Function

Public Shared Sub UseSecureTypeResolver()
    ' Retrieve legacy data using the secure type resolver
    Dim data As MyCustomType = Nothing
    If Clipboard.TryGetData("LegacyData", AddressOf SecureTypeResolver, data) Then
        ProcessLegacyData(data)
    Else
        Console.WriteLine("No compatible data found on clipboard")
    End If
End Sub

型リゾルバーのセキュリティ ガイドライン

型リゾルバーを実装する場合は、次の重要なセキュリティ ガイドラインに従ってください。

明示的な許可リストを使用する

  • 既定では拒否: 明示的にリストされている型のみを許可します。
  • ワイルドカードなし: パターン マッチングまたは名前空間ベースのアクセス許可を避けます。
  • 完全一致: 型名に完全一致する文字列が必要です。

すべての入力を検証する

  • 型名の検証: 型名が想定される形式と一致していることを確認します。
  • アセンブリの制限: 既知の信頼できるアセンブリに型を制限します。
  • バージョン チェック: バージョン固有の型の制限を検討してください。

不明な型を安全に処理する

  • 例外をスローする: 常に許可されていない型に対してスローします。
  • ログの試行: 未承認のアクセス試行をログに記録することを検討してください。
  • エラー メッセージをクリアする: デバッグの特定の拒否理由を指定します。

定期的なメンテナンス

  • 監査を定期的に行う: 許可される型リストを確認して更新します。
  • 使用されていない型を削除する: 不要になった型のアクセス許可を削除します。
  • ドキュメントの決定: 各種類が許可される理由の明確なドキュメントを保持します。

構成をテストする

BinaryFormatterサポートを構成した後、アプリケーションをテストして正しく動作することを確認します。

  1. クリップボード操作を確認する: カスタム型を使用してデータの格納と取得の両方をテストします。
  2. テスト型リゾルバー: 承認されていない型が正しく拒否されていることを確認します。
  3. セキュリティの監視: 予期しない型解決の試みを監視します。
  4. パフォーマンス テスト: 型リゾルバーがパフォーマンスに大きな影響を与えないことを確認します。
public static void TestBinaryFormatterConfiguration()
{
    // Test data to verify configuration
    var testPerson = new Person { Name = "Test User", Age = 30 };

    try
    {
        // Test storing data (this should work with proper configuration)
        Clipboard.SetData("TestPerson", testPerson);
        Console.WriteLine("Successfully stored test data on clipboard");

        // Test retrieving with type resolver
        if (Clipboard.TryGetData("TestPerson", SecureTypeResolver, out Person retrievedPerson))
        {
            Console.WriteLine($"Successfully retrieved: {retrievedPerson.Name}, Age: {retrievedPerson.Age}");
        }
        else
        {
            Console.WriteLine("Failed to retrieve test data");
        }

        // Test that unauthorized types are rejected
        try
        {
            Clipboard.TryGetData("TestPerson", UnauthorizedTypeResolver, out Person _);
            Console.WriteLine("ERROR: Unauthorized type was not rejected!");
        }
        catch (InvalidOperationException ex)
        {
            Console.WriteLine($"SUCCESS: Unauthorized type properly rejected - {ex.Message}");
        }
    }
    catch (Exception ex)
    {
        Console.WriteLine($"Configuration test failed: {ex.Message}");
    }
}

private static Type SecureTypeResolver(TypeName typeName)
{
    var allowedTypes = new Dictionary<string, Type>
    {
        ["ClipboardExamples.Person"] = typeof(Person),
    };

    if (allowedTypes.TryGetValue(typeName.FullName, out Type allowedType))
    {
        return allowedType;
    }

    throw new InvalidOperationException($"Type '{typeName.FullName}' is not permitted");
}

private static Type UnauthorizedTypeResolver(TypeName typeName)
{
    // Intentionally restrictive resolver to test rejection
    throw new InvalidOperationException($"No types are permitted by this test resolver");
}
Public Shared Sub TestBinaryFormatterConfiguration()
    ' Test data to verify configuration
    Dim testPerson As New Person With {.Name = "Test User", .Age = 30}

    Try
        ' Test storing data (this should work with proper configuration)
        Clipboard.SetData("TestPerson", testPerson)
        Console.WriteLine("Successfully stored test data on clipboard")

        ' Test retrieving with type resolver
        Dim retrievedPerson As Person = Nothing
        If Clipboard.TryGetData("TestPerson", AddressOf SecureTypeResolver, retrievedPerson) Then
            Console.WriteLine($"Successfully retrieved: {retrievedPerson.Name}, Age: {retrievedPerson.Age}")
        Else
            Console.WriteLine("Failed to retrieve test data")
        End If

        ' Test that unauthorized types are rejected
        Try
            Dim testResult As Person = Nothing
            Clipboard.TryGetData("TestPerson", AddressOf UnauthorizedTypeResolver, testResult)
            Console.WriteLine("ERROR: Unauthorized type was not rejected!")
        Catch ex As InvalidOperationException
            Console.WriteLine($"SUCCESS: Unauthorized type properly rejected - {ex.Message}")
        End Try

    Catch ex As Exception
        Console.WriteLine($"Configuration test failed: {ex.Message}")
    End Try
End Sub

Private Shared Function SecureTypeResolver(typeName As TypeName) As Type
    Dim allowedTypes As New Dictionary(Of String, Type) From {
        {"ClipboardExamples.Person", GetType(Person)}
    }

    Dim allowedType As Type = Nothing
    If allowedTypes.TryGetValue(typeName.FullName, allowedType) Then
        Return allowedType
    End If

    Throw New InvalidOperationException($"Type '{typeName.FullName}' is not permitted")
End Function

Private Shared Function UnauthorizedTypeResolver(typeName As TypeName) As Type
    ' Intentionally restrictive resolver to test rejection
    Throw New InvalidOperationException($"No types are permitted by this test resolver")
End Function

移行戦略を計画する

BinaryFormatterサポートでは一時的な互換性が提供されますが、新しいタイプ セーフな API に移行するための移行計画を作成します。

  1. 使用状況を特定する: カスタム型を使用して、すべてのクリップボード操作をカタログ化します。
  2. 移行に優先順位を付ける: 最初に最もセキュリティに依存する操作に重点を置く。
  3. 増分更新: リスクを軽減するために、一度に 1 つの操作を移行します。
  4. 十分にテストする: 新しい実装で同等の機能が提供されていることを確認します。
  5. BinaryFormatter の削除: 移行が完了したら、サポートを無効にします。

リソースをクリーンアップする

新しいタイプ セーフなクリップボード API に移行したら、 BinaryFormatter 構成を削除してセキュリティを強化します。

  1. System.Runtime.Serialization.Formatters パッケージ参照を削除します。
  2. プロジェクト ファイルから EnableUnsafeBinaryFormatterSerialization プロパティを削除します。
  3. runtimeconfig.jsonからWindows.ClipboardDragDrop.EnableUnsafeBinaryFormatterSerialization設定を削除します。
  4. 不要になった型リゾルバーの実装を削除します。