依存関係プロパティのセキュリティ (WPF .NET)

Windows Presentation Foundation (WPF) プロパティ システムを介した読み取り/書き込み依存関係プロパティのアクセシビリティにより、これらは実質的にパブリック プロパティになります。 その結果、読み取り/書き込み依存関係プロパティ値に関するセキュリティを保証することはできません。 書き込みアクセスを制限できるように、WPF プロパティ システムによって、読み取り専用の依存関係プロパティのセキュリティが強化されます。

重要

.NET 7 と .NET 6 用のデスクトップ ガイド ドキュメントは作成中です。

プロパティ ラッパーのアクセスとセキュリティ

共通言語ランタイム (CLR) プロパティ ラッパーは、通常、プロパティ値の取得または設定を簡単にするために、読み取り/書き込み依存関係プロパティの実装に含まれています。 CLR プロパティ ラッパーが含まれている場合、これは基になる依存関係プロパティを操作する GetValue および SetValue 静的呼び出しを実装する便利なメソッドです。 基本的に、CLR プロパティ ラッパーは、プライベート フィールドではなく依存関係プロパティによってサポートされる CLR プロパティとして依存関係プロパティを公開します。

セキュリティ メカニズムを適用し、CLR プロパティ ラッパーへのアクセスを制限すると、便利なメソッドの使用が妨げられる可能性がありますが、これらの手法では GetValue または SetValue への直接呼び出しを妨げることはありません。 言い換えると、読み取り/書き込み依存関係プロパティは常に WPF プロパティ システムを介してアクセスできます。 読み取り/書き込み依存関係プロパティを実装する場合は、CLR プロパティ ラッパーへのアクセスを制限しないでください。 代わりに、CLR プロパティ ラッパーをパブリック メンバーとして宣言して、呼び出し元が依存関係プロパティの真のアクセス レベルを認識できるようにします。

プロパティ システムによる依存関係プロパティの公開

WPF プロパティ システムでは、DependencyProperty 識別子を介して読み取り/書き込み依存関係プロパティへのアクセスを提供します。 識別子は、GetValue および SetValue 呼び出しで使用できます。 静的識別子フィールドがパブリックでない場合でも、プロパティ システムのいくつかの側面では、クラスまたは派生クラスのインスタンスに存在する DependencyProperty を返します。 たとえば、GetLocalValueEnumerator メソッドは、値がローカルに設定された依存関係プロパティ インスタンスの識別子を返します。 また、OnPropertyChanged 仮想メソッドをオーバーライドして、値が変更された依存関係プロパティの DependencyProperty 識別子を報告するイベント データを受信することもできます。 呼び出し元が読み取り/書き込み依存関係プロパティの真のアクセス レベルを認識できるようにするには、その識別子フィールドをパブリック メンバーとして宣言します。

注意

依存関係プロパティ識別子フィールドを private として宣言すると、読み取り/書き込み依存関係プロパティにアクセスできる方法の数は減りますが、CLR 言語の定義に従って、プロパティはプライベートになりません。

検証のセキュリティ

DemandValidateValueCallback に適用して、Demand エラー時に検証が失敗することを想定することは、プロパティ値の変更を制限するための適切なセキュリティ メカニズムではありません。 また、悪意のある呼び出し元がアプリケーション ドメイン内で動作している場合、ValidateValueCallback によって適用される新しい値の無効化も、そのような呼び出し元によって抑制される可能性があります。

読み取り専用の依存関係プロパティへのアクセス

アクセスを制限するには、RegisterReadOnly メソッドを呼び出して、プロパティを読み取り専用の依存関係プロパティとして登録します。 RegisterReadOnly メソッドは、非パブリック クラス フィールドに割り当てることができる DependencyPropertyKey を返します。 読み取り専用の依存関係プロパティの場合、WPF プロパティ システムでは、DependencyPropertyKey への参照を持つものにのみ書き込みアクセスが提供されます。 この動作を説明するために、次のテスト コードを示します。

  • 読み取り/書き込みおよび読み取り専用の両方の依存関係プロパティを実装するクラスをインスタンス化します。
  • 各識別子に private アクセス修飾子を割り当てます。
  • get アクセサーのみを実装します。
  • GetLocalValueEnumerator メソッドを使用して、WPF プロパティ システムを介して基になる依存関係プロパティにアクセスします。
  • GetValue および SetValue を呼び出して、各依存関係プロパティ値へのアクセスをテストします。
    /// <summary>
    ///  Test get/set access to dependency properties exposed through the WPF property system.
    /// </summary>
    public static void DependencyPropertyAccessTests()
    {
        // Instantiate a class that implements read-write and read-only dependency properties.
        Aquarium _aquarium = new();
        // Access each dependency property using the LocalValueEnumerator method.
        LocalValueEnumerator localValueEnumerator = _aquarium.GetLocalValueEnumerator();
        while (localValueEnumerator.MoveNext())
        {
            DependencyProperty dp = localValueEnumerator.Current.Property;
            string dpType = dp.ReadOnly ? "read-only" : "read-write";
            // Test read access.
            Debug.WriteLine($"Attempting to get a {dpType} dependency property value...");
            Debug.WriteLine($"Value ({dpType}): {(int)_aquarium.GetValue(dp)}");
            // Test write access.
            try
            {
                Debug.WriteLine($"Attempting to set a {dpType} dependency property value to 2...");
                _aquarium.SetValue(dp, 2);
            }
            catch (InvalidOperationException e)
            {
                Debug.WriteLine(e.Message);
            }
            finally
            {
                Debug.WriteLine($"Value ({dpType}): {(int)_aquarium.GetValue(dp)}");
            }
        }

        // Test output:

        // Attempting to get a read-write dependency property value...
        // Value (read-write): 1
        // Attempting to set a read-write dependency property value to 2...
        // Value (read-write): 2

        // Attempting to get a read-only dependency property value...
        // Value (read-only): 1
        // Attempting to set a read-only dependency property value to 2...
        // 'FishCountReadOnly' property was registered as read-only
        // and cannot be modified without an authorization key.
        // Value (read-only): 1
    }
}

public class Aquarium : DependencyObject
{
    public Aquarium()
    {
        // Assign locally-set values.
        SetValue(FishCountProperty, 1);
        SetValue(FishCountReadOnlyPropertyKey, 1);
    }

    // Failed attempt to restrict write-access by assigning the
    // DependencyProperty identifier to a non-public field.
    private static readonly DependencyProperty FishCountProperty =
        DependencyProperty.Register(
          name: "FishCount",
          propertyType: typeof(int),
          ownerType: typeof(Aquarium),
          typeMetadata: new PropertyMetadata());

    // Successful attempt to restrict write-access by assigning the
    // DependencyPropertyKey to a non-public field.
    private static readonly DependencyPropertyKey FishCountReadOnlyPropertyKey =
        DependencyProperty.RegisterReadOnly(
          name: "FishCountReadOnly",
          propertyType: typeof(int),
          ownerType: typeof(Aquarium),
          typeMetadata: new PropertyMetadata());

    // Declare public get accessors.
    public int FishCount => (int)GetValue(FishCountProperty);
    public int FishCountReadOnly => (int)GetValue(FishCountReadOnlyPropertyKey.DependencyProperty);
}
    ''' <summary>
    ''' ' Test get/set access to dependency properties exposed through the WPF property system.
    ''' </summary>
    Public Shared Sub DependencyPropertyAccessTests()
        ' Instantiate a class that implements read-write and read-only dependency properties.
        Dim _aquarium As New Aquarium()
        ' Access each dependency property using the LocalValueEnumerator method.
        Dim localValueEnumerator As LocalValueEnumerator = _aquarium.GetLocalValueEnumerator()
        While localValueEnumerator.MoveNext()
            Dim dp As DependencyProperty = localValueEnumerator.Current.[Property]
            Dim dpType As String = If(dp.[ReadOnly], "read-only", "read-write")
            ' Test read access.
            Debug.WriteLine($"Attempting to get a {dpType} dependency property value...")
            Debug.WriteLine($"Value ({dpType}): {CInt(_aquarium.GetValue(dp))}")
            ' Test write access.
            Try
                Debug.WriteLine($"Attempting to set a {dpType} dependency property value to 2...")
                _aquarium.SetValue(dp, 2)
            Catch e As InvalidOperationException
                Debug.WriteLine(e.Message)
            Finally
                Debug.WriteLine($"Value ({dpType}): {CInt(_aquarium.GetValue(dp))}")
            End Try
        End While

        ' Test output

        ' Attempting to get a read-write dependency property value...
        ' Value (read-write): 1
        ' Attempting to set a read-write dependency property value to 2...
        ' Value (read-write): 2

        ' Attempting to get a read-only dependency property value...
        ' Value (read-only): 1
        ' Attempting to set a read-only dependency property value to 2...
        ' 'FishCountReadOnly' property was registered as read-only
        ' and cannot be modified without an authorization key.
        ' Value (read-only): 1
    End Sub

End Class

Public Class Aquarium
    Inherits DependencyObject

    Public Sub New()
        ' Assign locally-set values.
        SetValue(FishCountProperty, 1)
        SetValue(FishCountReadOnlyPropertyKey, 1)
    End Sub

    ' Failed attempt to restrict write-access by assigning the
    ' DependencyProperty identifier to a non-public field.
    Private Shared ReadOnly FishCountProperty As DependencyProperty =
        DependencyProperty.Register(
            name:="FishCount",
            propertyType:=GetType(Integer),
            ownerType:=GetType(Aquarium),
            typeMetadata:=New PropertyMetadata())

    ' Successful attempt to restrict write-access by assigning the
    ' DependencyPropertyKey to a non-public field.
    Private Shared ReadOnly FishCountReadOnlyPropertyKey As DependencyPropertyKey =
        DependencyProperty.RegisterReadOnly(
            name:="FishCountReadOnly",
            propertyType:=GetType(Integer),
            ownerType:=GetType(Aquarium),
            typeMetadata:=New PropertyMetadata())

    ' Declare public get accessors.
    Public ReadOnly Property FishCount As Integer
        Get
            Return GetValue(FishCountProperty)
        End Get
    End Property

    Public ReadOnly Property FishCountReadOnly As Integer
        Get
            Return GetValue(FishCountReadOnlyPropertyKey.DependencyProperty)
        End Get
    End Property

End Class

関連項目