종속성 속성 보안(WPF .NET)
WPF(Windows Presentation Foundation) 속성 시스템을 통해 읽기-쓰기 종속성 속성에 액세스하면 해당 속성을 효과적으로 공용 속성으로 만들 수 있습니다. 따라서 읽기-쓰기 종속성 속성 값에 대한 보안을 보장할 수 없습니다. WPF 속성 시스템은 읽기 전용 종속성 속성에 대해 더 많은 보안을 제공하므로 쓰기 액세스를 제한할 수 있습니다.
속성 래퍼의 액세스 및 보안
CLR(공용 언어 런타임) 속성 래퍼는 일반적으로 속성 값 가져오기 또는 설정을 간소화하기 위해 읽기-쓰기 종속성 속성 구현에 포함됩니다. 포함된 경우, CLR 속성 래퍼는 기본 종속성 속성과 상호 작용하는 GetValue 및 SetValue 정적 호출을 구현하는 편리한 메서드입니다. 기본적으로 CLR 속성 래퍼는 종속성 속성을 프라이빗 필드가 아닌 종속성 속성으로 지원되는 CLR 속성으로 노출합니다.
보안 메커니즘을 적용하고 CLR 속성 래퍼에 대한 액세스를 제한하면 편리한 메서드의 사용을 방지할 수는 있겠지만 이러한 기법은 GetValue
또는 SetValue
에 대한 직접 호출을 방지할 수 없습니다. 즉, 읽기-쓰기 종속성 속성은 항상 WPF 속성 시스템을 통해 액세스할 수 있습니다. 읽기-쓰기 종속성 속성을 구현하는 경우, CLR 속성 래퍼에 대한 액세스를 제한하지 마십시오. 그 대신, 호출자가 종속성 속성의 실제 액세스 수준을 인식할 수 있도록 CLR 속성 래퍼를 공용 멤버로 선언하세요.
종속성 속성의 속성 시스템 노출
WPF 속성 시스템은 DependencyProperty 식별자를 통해 읽기-쓰기 종속성 속성에 대한 액세스를 제공합니다. 식별자는 GetValue 및 SetValue 호출에서 사용할 수 있습니다. 정적 식별자 필드가 비공개인 경우에도 속성 시스템의 여러 측면은 클래스 또는 파생 클래스의 인스턴스에 있는 그대로 DependencyProperty
를 반환합니다. 예를 들어 GetLocalValueEnumerator 메서드는 로컬로 설정된 값을 가진 종속성 속성 인스턴스에 대한 식별자를 반환합니다. 또한 값이 변경된 종속성 속성에 대한 DependencyProperty
식별자를 보고하는 이벤트 데이터를 수신하도록 OnPropertyChanged 가상 메서드를 재정의할 수 있습니다. 호출자가 읽기-쓰기 종속성 속성의 실제 액세스 수준을 인식하도록 하려면 해당 식별자 필드를 공용 멤버로 선언하세요.
참고
종속성 속성 식별자 필드를 private
으로 선언하면 읽기-쓰기 종속성 속성에 액세스할 수 있는 방법의 수가 줄어들지만 CLR 언어 정의에 따라 해당 속성은 비공개가 아닙니다.
유효성 검사 보안
Demand를 ValidateValueCallback에 적용하고 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
참고 항목
.NET Desktop feedback