Share via


Seguridad de las propiedades de dependencia (WPF .NET)

La accesibilidad de las propiedades de dependencia de lectura y escritura a través del sistema de propiedades Windows Presentation Foundation (WPF) hace que sean propiedades públicas. Como resultado, no es posible garantizar la seguridad de los valores de propiedad de dependencia de lectura y escritura. El sistema de propiedades de WPF proporciona más seguridad para las propiedades de dependencia de solo lectura para que se pueda restringir el acceso de escritura.

Importante

La documentación de la guía de escritorio para .NET 7 y .NET 6 está en proceso de elaboración.

Acceso y seguridad de los contenedores de propiedades

Un contenedor de propiedades de Common Language Runtime (CLR) se incluye normalmente en implementaciones de propiedades de dependencia de lectura y escritura para simplificar la obtención o establecimiento de valores de propiedad. Si se incluye, el contenedor de propiedades CLR es un método de conveniencia que implementa las llamadas estáticas GetValue y SetValue que interactúan con la propiedad de dependencia subyacente. Básicamente, un contenedor de propiedades CLR expone una propiedad de dependencia como una propiedad CLR respaldada por una propiedad de dependencia en lugar de un campo privado.

Aplicar mecanismos de seguridad y restringir el acceso al contenedor de propiedades CLR podría impedir el uso del método de conveniencia, pero esas técnicas no impedirán llamadas directas a GetValue o SetValue. En otras palabras, siempre se puede acceder a una propiedad de dependencia de lectura y escritura a través del sistema de propiedades de WPF. Si va a implementar una propiedad de dependencia de lectura y escritura, evite restringir el acceso al contenedor de propiedades CLR. En su lugar, declare el contenedor de propiedades CLR como miembro público para que los autores de llamadas sean conscientes del verdadero nivel de acceso de la propiedad de dependencia.

Exposición del sistema de propiedades de las propiedades de dependencia

El sistema de propiedades de WPF proporciona acceso a una propiedad de dependencia de lectura y escritura a través de su identificador DependencyProperty. El identificador se puede usar en las llamadas a GetValue y SetValue. Incluso si el campo de identificador estático no es público, varios aspectos del sistema de propiedades devolverán un DependencyProperty, ya que existe en una instancia de una clase o clase derivada. Por ejemplo, el método GetLocalValueEnumerator devuelve identificadores para las instancias de propiedades de dependencia con un valor establecido localmente. Además, puede invalidar el método virtual OnPropertyChanged para recibir datos de eventos que notificarán al identificador DependencyProperty de las propiedades de dependencia que han cambiado el valor. Para que los autores de llamada conozcan el nivel de acceso verdadero de una propiedad de dependencia de lectura y escritura, declare su campo de identificador como miembro público.

Nota:

Aunque declarar un campo de identificador de propiedad de dependencia como private reduce el número de formas en que se puede acceder a una propiedad de dependencia de lectura y escritura, la propiedad no será privada según la definición del lenguaje CLR.

Seguridad de validación

Aplicar un Demand a un ValidateValueCallback y esperar que se produzca un error Demand en la validación no es un mecanismo de seguridad adecuado para restringir los cambios de valor de propiedad. Además, los autores de llamada malintencionados pueden suprimir la invalidación de nuevo valor que se aplica a través de ValidateValueCallback, si esos autores de llamadas funcionan dentro del dominio de aplicación.

Acceso a las propiedades de dependencia de solo lectura

Para restringir el acceso, registre la propiedad como una propiedad de dependencia de solo lectura llamando al método RegisterReadOnly. El método RegisterReadOnly devuelve un DependencyPropertyKey, que se puede asignar a un campo de clase no pública. Para las propiedades de dependencia de solo lectura, el sistema de propiedades de WPF solo proporcionará acceso de escritura a aquellos que tengan una referencia a DependencyPropertyKey. Para ilustrar este comportamiento, el siguiente código de prueba:

  • Crea instancias de una clase que implementa propiedades de dependencia de lectura y escritura y de solo lectura.
  • Asigna un modificador de acceso private a cada identificador.
  • Solo implementa los descriptores de acceso get.
  • Usa el método GetLocalValueEnumerator para tener acceso a las propiedades de dependencia subyacentes a través del sistema de propiedades de WPF.
  • Llama a GetValue y a SetValue para probar el acceso a cada valor de propiedad de dependencia.
    /// <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

Vea también