Me he encontrado con un problema con la unión a un P asswordBox
. Parece que es un riesgo de seguridad, pero estoy usando el patrón MVVM, así que deseo evitarlo. Encontré un código interesante aquí (¿alguien ha usado esto o algo similar?)
http://www.wpftutorial.net/PasswordBox.html
Técnicamente se ve muy bien, pero no estoy seguro de cómo recuperar la contraseña.
Básicamente tengo propiedades en mi LoginViewModel
para Username
y Password
. Username
está bien y funciona como es un TextBox
.
Usé el código anterior como se indicó e ingresé esto
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
Cuando tuve el PasswordBox
como TextBox
y Binding Path=Password
entonces la propiedad en mi LoginViewModel
fue actualizado.
Mi código es muy simple, básicamente tengo un Command
para mi Button
. Cuando presiono se CanLogin
llama y si devuelve verdadero, llama Login
.
Puedes ver que reviso mi propiedad Username
aquí, lo que funciona muy bien.
En Login
envío a lo largo de mi servicio una Username
y Password
, Username
contiene datos de mi View
, pero Password
esNull|Empty
private DelegateCommand loginCommand;
public string Username { get; set; }
public string Password { get; set; }
public ICommand LoginCommand
{
get
{
if (loginCommand == null)
{
loginCommand = new DelegateCommand(
Login, CanLogin );
}
return loginCommand;
}
}
private bool CanLogin()
{
return !string.IsNullOrEmpty(Username);
}
private void Login()
{
bool result = securityService.IsValidLogin(Username, Password);
if (result) { }
else { }
}
Esto es lo que estoy haciendo
<TextBox Text="{Binding Path=Username, UpdateSourceTrigger=PropertyChanged}"
MinWidth="180" />
<PasswordBox ff:PasswordHelper.Attach="True"
ff:PasswordHelper.Password="{Binding Path=Password}" Width="130"/>
Tengo mi TextBox
, esto no es problema, pero en mi ViewModel
el Password
usuario está vacía.
¿Estoy haciendo algo mal o me falta un paso?
Puse un punto de interrupción y, efectivamente, el código ingresó a la clase auxiliar estática, pero nunca actualiza mi Password
en mi ViewModel
.
Respuestas:
Lo siento, pero lo estás haciendo mal.
Las personas deben tener las siguientes pautas de seguridad tatuadas en el interior de sus párpados:
nunca guarde contraseñas de texto sin formato en la memoria.
La razón por la que WPF / Silverlight
PasswordBox
no expone un DP para laPassword
propiedad está relacionada con la seguridad.Si WPF / Silverlight mantuviera un DP
Password
, sería necesario que el marco mantuviera la contraseña sin cifrar en la memoria. Lo que se considera un vector de ataque de seguridad bastante problemático. LaPasswordBox
usos cifrado de memoria (de clases) y la única manera de acceder a la contraseña es a través de la propiedad de CLR.Sugeriría que al acceder a la
PasswordBox.Password
propiedad CLR se abstenga de colocarla en cualquier variable o como valor para cualquier propiedad.Mantener su contraseña en texto sin formato en la RAM del equipo cliente es un no-no de seguridad.
Así que deshazte de eso
public string Password { get; set; }
que tienes allá arriba.Al acceder
PasswordBox.Password
, simplemente sáquelo y envíelo al servidor lo antes posible. No guarde el valor de la contraseña y no la trate como lo haría con cualquier otro texto de máquina cliente. No guarde contraseñas de texto claro en la memoria.Sé que esto rompe el patrón MVVM, pero nunca debe vincularse a
PasswordBox.Password
DP adjunto, almacenar su contraseña en ViewModel o cualquier otra travesura similar.Si está buscando una solución sobredimensionada, aquí tiene una:
1. Cree la
IHavePassword
interfaz con un método que devuelva el texto claro de la contraseña.2. Haga que
UserControl
implemente unaIHavePassword
interfaz.3. Registre la
UserControl
instancia con su IoC como implementando laIHavePassword
interfaz.4. Cuando se realiza una solicitud del servidor que requiere su contraseña, llame a su IoC para la
IHavePassword
implementación y solo para obtener la codiciada contraseña.Solo mi opinión sobre eso.
- justin
fuente
Mis 2 centavos:
Una vez desarrollé un cuadro de diálogo de inicio de sesión típico (cuadros de usuario y contraseña, más el botón "Aceptar") usando WPF y MVVM. Resolví el problema de vinculación de contraseña simplemente pasando el control PasswordBox como parámetro al comando adjunto al botón "Aceptar". Entonces, en la vista que tenía:
Y en ViewModel, el
Execute
método del comando adjunto fue el siguiente:Esto viola ligeramente el patrón MVVM ya que ahora ViewModel sabe algo acerca de cómo se implementa la Vista, pero en ese proyecto en particular podría pagarlo. Espero que sea útil para alguien también.
fuente
Tal vez me falta algo, pero parece que la mayoría de estas soluciones complican demasiado las cosas y eliminan las prácticas seguras.
Este método no viola el patrón MVVM y mantiene la seguridad completa. Sí, técnicamente está detrás del código, pero no es más que un enlace de "caso especial". ViewModel todavía no tiene conocimiento de la implementación de View, lo que, en mi opinión, sí sabe si está intentando pasar PasswordBox a ViewModel.
Code Behind! = Infracción automática de MVVM. Todo depende de lo que hagas con él. En este caso, solo estamos codificando manualmente un enlace, por lo que todo se considera parte de la implementación de la interfaz de usuario y, por lo tanto, está bien.
En ViewModel, solo una propiedad simple. Lo hice "escribir solo" ya que no debería ser necesario recuperarlo desde fuera del ViewModel por ninguna razón, pero no tiene que ser así. Tenga en cuenta que es un SecureString, no solo una cadena.
En el xaml, configura un controlador de eventos PasswordChanged.
En el código detrás:
Con este método, su contraseña permanece en SecureString en todo momento y, por lo tanto, proporciona la máxima seguridad. Si realmente no le importa la seguridad o si necesita la contraseña de texto sin cifrar para un método posterior que lo requiere (nota: la mayoría de los métodos .NET que requieren una contraseña también admiten una opción SecureString, por lo que es posible que no necesite una contraseña de texto sin cifrar) incluso si cree que lo hace), puede usar la propiedad Contraseña. Me gusta esto:
(Propiedad ViewModel)
(Código detrás)
Si desea mantener las cosas fuertemente tipadas, puede sustituir el reparto (dinámico) con la interfaz de su ViewModel. Pero realmente, los enlaces de datos "normales" tampoco están fuertemente tipados, por lo que no es un gran problema.
El mejor de todos los mundos: su contraseña es segura, su ViewModel solo tiene una propiedad como cualquier otra propiedad, y su Vista es autónoma sin necesidad de referencias externas.
fuente
Puedes usar este XAML:
Y este comando ejecuta el método:
fuente
xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity"
CommandParameter="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}}"
(nota: noRelativeSource Self
).Esto funciona bien para mí.
fuente
ICommand
se implementa en el modelo de vista, esta solución violaría el patrón MVVM.Una solución simple sin violar el patrón MVVM es introducir un evento (o delegar) en el ViewModel que recolecta la contraseña.
En el modelo de vista :
public event EventHandler<HarvestPasswordEventArgs> HarvestPassword;
con estos EventArgs:
en la Vista , suscríbase al evento al crear ViewModel y complete el valor de la contraseña.
En ViewModel , cuando necesita la contraseña, puede activar el evento y cosechar la contraseña desde allí:
fuente
WeakEventManager<TEventSource, TEventArgs>
para evitar pérdidas de memoria. Muchas veces la vista no tendrá la misma duración que el modelo de vista.WeakEventManager<IViewModel, EventArgs>.AddHandler(iViewModelInstance, nameof(IViewModel.Event), eventHandlerMethod);
Pasé mucho tiempo buscando varias soluciones. No me gustó la idea de los decoradores, los comportamientos arruinan la interfaz de usuario de validación, el código detrás ...
Lo mejor hasta ahora es apegarse a una propiedad adjunta personalizada y unirse a su
SecureString
propiedad en su modelo de vista. Mantenlo ahí todo el tiempo que puedas. Siempre que necesite acceso rápido a la contraseña simple, conviértala temporalmente en una cadena no segura utilizando el código a continuación:Asegúrese de permitir que el GC recopile su elemento de IU, así que resista la necesidad de utilizar un controlador de eventos estático para el
PasswordChanged
evento en elPasswordBox
. También descubrí una anomalía en la que el control no estaba actualizando la interfaz de usuario cuando usaba laSecurePassword
propiedad para configurarla, por lo que estoy copiando la contraseñaPassword
.Y el uso de XAML:
Mi propiedad en el modelo de vista se veía así:
El
RequiredSecureString
es solo un simple validador personalizado que tiene la siguiente lógica:Aqui lo tienes. Una solución MVVM pura completa y probada.
fuente
Publiqué un GIST aquí que es un cuadro de contraseña enlazable.
fuente
ContentControl
que luego puede usar un PasswordBox como el contenido y el estilo que en XAML como le convenga. El propósito de laContentControl
es simplemente suscribirse alPasswordChanged
evento y exponer una propiedad enlazable en dos direcciones. Con todo, son 65 líneas de código y más o menos lo que hace esta clase de decorar. Vea aquí mi esencia de lo siguiente gist.github.com/leidegre/c7343b8c720000fe3132Esta implementación es ligeramente diferente. Pasa un cuadro de contraseña a la Vista a través del enlace de una propiedad en ViewModel, no utiliza ningún parámetro de comando. ViewModel permanece ignorante de la vista. Tengo un proyecto VB vs 2010 que se puede descargar de SkyDrive. Wpf MvvM PassWordBox Example.zip https://skydrive.live.com/redir.aspx?cid=e95997d33a9f8d73&resid=E95997D33A9F8D73!511
La forma en que uso PasswordBox en una aplicación Wpf MvvM es bastante simplista y funciona bien para mí. Eso no significa que piense que es la forma correcta o la mejor. Es solo una implementación del uso de PasswordBox y el patrón MvvM.
Básicamente, usted crea una propiedad pública de solo lectura a la que la Vista puede enlazar como PasswordBox (El control real) Ejemplo:
Utilizo un campo de respaldo solo para hacer la autoinicialización de la propiedad.
Luego, desde Xaml, vincula el contenido de un ContentControl o un ejemplo de contenedor de control:
Desde allí, tiene el control total de la caja de contraseña. También uso un PasswordAccessor (solo una función de cadena) para devolver el valor de la contraseña al iniciar sesión o para cualquier otra cosa para la que desee la contraseña. En el ejemplo, tengo una propiedad pública en un modelo de objeto de usuario genérico. Ejemplo:
En el objeto de usuario, la propiedad de cadena de contraseña es de solo lectura sin ningún almacén de respaldo, solo devuelve la contraseña del PasswordBox. Ejemplo:
Luego, en ViewModel, me aseguro de que el Accesor esté creado y configurado en la propiedad PasswordBox.Password 'Ejemplo:
Cuando necesito que la cadena de Contraseña diga para iniciar sesión, solo obtengo la propiedad Contraseña de objetos de usuario que realmente invoca la Función para tomar la contraseña y devolverla, luego el Usuario Objeto no almacena la contraseña real. Ejemplo: estaría en ViewModel
Deberias hacer eso. ViewModel no necesita ningún conocimiento de los controles de la vista. La vista solo se vincula a la propiedad en el modelo de vista, no es diferente al enlace de vista a una imagen u otro recurso. En este caso, ese recurso (Propiedad) resulta ser un control de usuario. Permite realizar pruebas a medida que ViewModel crea y posee la Propiedad y la Propiedad es independiente de la Vista. En cuanto a la seguridad, no sé qué tan buena es esta implementación. Pero al usar una Función, el Valor no se almacena en la Propiedad a la que solo accede la Propiedad.
fuente
Para resolver el problema de OP sin romper el MVVM, usaría un convertidor de valor personalizado y un contenedor para el valor (la contraseña) que debe recuperarse del cuadro de contraseña.
En el modelo de vista:
Debido a que el modelo de vista usa
IWrappedParameter<T>
, no necesita tener ningún conocimiento sobrePasswordBoxWrapper
niPasswordBoxConverter
. De esta manera, puede aislar elPasswordBox
objeto del modelo de vista y no romper el patrón MVVM.En la vista:
fuente
Si bien estoy de acuerdo en que es importante evitar almacenar la contraseña en cualquier lugar, todavía necesito la capacidad de crear una instancia del modelo de vista sin una vista y ejecutar mis pruebas contra ella.
La solución que funcionó para mí fue registrar la función PasswordBox.Password con el modelo de vista y hacer que el modelo de vista lo invoque al ejecutar el código de inicio de sesión.
Esto hace significar una línea de código en el código subyacente de la vista.
Entonces, en mi Login.xaml tengo
y en Login.xaml.cs tengo
luego en LoginViewModel.cs tengo definido el PasswordHandler
y cuando es necesario iniciar sesión, el código invoca el controlador para obtener la contraseña de la vista ...
De esta manera, cuando quiero probar el modelo de vista, simplemente puedo configurar PasswordHandler a un método anónimo que me permita entregar cualquier contraseña que quiera usar en la prueba.
fuente
Pensé en tirar mi solución en la mezcla, ya que este es un problema tan común ... y tener muchas opciones siempre es algo bueno.
Simplemente envolví un
PasswordBox
enUserControl
e implementé unDependencyProperty
para poder enlazar. Estoy haciendo todo lo posible para evitar almacenar cualquier texto claro en la memoria, por lo que todo se hace a través de aySecureString
laPasswordBox.Password
propiedad. Durante elforeach
ciclo, cada personaje queda expuesto, pero es muy breve. Honestamente, si le preocupa que su aplicación WPF se vea comprometida por esta breve exposición, tiene problemas de seguridad más importantes que deben manejarse.Lo bueno de esto es que no estás rompiendo ninguna regla MVVM, ni siquiera las "puristas", ya que esto es un
UserControl
, así que está permitido tener código subyacente. Cuando lo está utilizando, puede tener una comunicación pura entreView
yViewModel
sin que tengaVideModel
conocimiento de ninguna parteView
o de la fuente de la contraseña. Solo asegúrate de estar atado aSecureString
tuViewModel
.BindablePasswordBox.xaml
BindablePasswordBox.xaml.cs (Versión 1 - No hay soporte de enlace bidireccional).
Uso de la Versión 1:
BindablePasswordBox.xaml.cs (Versión 2 - Tiene soporte de enlace bidireccional).
Uso de la versión 2:
fuente
if (Password != secure)
siempre será falso ya que SecureString no anula iguales. ¿Alguna idea?puedes hacerlo con la propiedad adjunta, verlo .. PasswordBox con MVVM
fuente
Utilicé este método y pasé el cuadro de contraseña, aunque esto viola el MVVM, fue esencial para mí porque estaba usando un control de contenido con plantilla de datos para mi inicio de sesión dentro de mi shell, que es un entorno de shell complejo. Por lo tanto, acceder al código detrás del shell habría sido una mierda.
Al pasar el cuadro de contraseña, creo que es lo mismo que acceder al control desde el código detrás de lo que sé. Acepto las contraseñas, no las guardo en la memoria, etc. En esta implementación no tengo propiedad para la contraseña en el modelo de vista.
Comando de botón
ViewModel
fuente
Para mí, estas dos cosas se sienten mal:
PasswordBox
como un parámetro de comando a ViewModelLa transferencia de SecurePassword (instancia de SecureString) como lo describe Steve en CO parece aceptable. yo prefiero
Behaviors
codificar detrás, y también tuve el requisito adicional de poder restablecer la contraseña desde el modelo de vista.Xaml (
Password
es la propiedad ViewModel):Comportamiento:
fuente
Para novatos completos como yo, aquí hay una muestra de trabajo completa de lo que se
Konamiman
sugirió anteriormente. GraciasKonamiman
.XAML
ViewModel
fuente
Es una propiedad adjunta . Este tipo de propiedad se puede aplicar a cualquier tipo de
DependencyObject
, no solo al tipo en el que se declara. Por lo tanto, aunque se declare en laPasswordHelper
clase estática, se aplica a laPasswordBox
que se usa.Para usar esta propiedad adjunta, solo necesita vincularla a la
Password
propiedad en su ViewModel:fuente
He hecho como:
XAML:
C#:
¡Esto funciona para mi!
fuente
Como se mencionó anteriormente, VM no debe ser consciente de la Vista, pero pasar todo PasswordBox parece el enfoque más simple. Entonces, en lugar de enviar el parámetro pasado a PasswordBox, use Reflection para extraer la propiedad Password de él. En este caso, VM espera algún tipo de Contenedor de contraseña con la Contraseña de propiedad (estoy usando RelayCommands de MVMM Light-Toolkit):
Se puede probar fácilmente con la clase anónima:
fuente
En la aplicación universal de windows
puede usar este código con la propiedad "Contraseña" y vincular con modelView
fuente
Para cualquiera que conozca los riesgos que impone esta implementación, para que la contraseña se sincronice con su ViewModel simplemente agregue Mode = OneWayToSource .
XAML
fuente
OneWayToSource
?Aquí está mi opinión al respecto:
El uso de una propiedad adjunta para vincular la contraseña anula el propósito de proteger la contraseña. La propiedad Contraseña de un cuadro de contraseña no se puede enlazar por un motivo.
Pasar el cuadro de contraseña como parámetro de comando hará que ViewModel conozca el control. Esto no funcionará si planea hacer que su plataforma cruzada reutilizable ViewModel. No haga que su VM conozca su Vista o cualquier otro control.
No creo que sea necesario introducir una nueva propiedad, una interfaz, suscribirse a eventos de cambio de contraseña o cualquier otra cosa complicada para una tarea simple de proporcionar la contraseña.
XAML
Código subyacente: el uso de código subyacente no necesariamente viola MVVM. Siempre y cuando no pongas ninguna lógica de negocios.
ViewModel
fuente
Puede encontrar una solución para PasswordBox en la aplicación de ejemplo ViewModel de WPF Application Framework (WAF) .
Sin embargo, Justin tiene razón. No pase la contraseña como texto sin formato entre View y ViewModel. Utilice SecureString en su lugar (consulte MSDN PasswordBox).
fuente
Usé una verificación de autenticación seguida de un sub llamado por una clase mediadora a la Vista (que también implementa una verificación de autenticación) para escribir la contraseña en la clase de datos.
No es una solución perfecta; sin embargo, solucionó mi problema de no poder mover la contraseña.
fuente
Estoy usando una solución sucinta compatible con MVVM que aún no se ha mencionado. Primero, llamo PasswordBox en XAML:
Luego agrego una llamada de método único al constructor de la vista:
Y eso es. El modelo de vista recibirá una notificación cuando se adjunte a una vista a través de DataContext y otra notificación cuando se desconecte. El contenido de esta notificación se puede configurar a través de las lambdas, pero generalmente es solo una llamada de método o de establecimiento en el modelo de vista, pasando el control problemático como parámetro.
Se puede hacer MVVM amigable muy fácilmente al hacer que la vista exponga la interfaz en lugar de los controles secundarios.
El código anterior se basa en la clase auxiliar publicada en mi blog.
fuente
Pasé años intentando que esto funcionara. Al final, me di por vencido y simplemente utilicé PasswordBoxEdit de DevExpress.
Es la solución más simple, ya que permite la unión sin hacer ningún truco horrible.
Solución en el sitio web DevExpress
Para el registro, no estoy afiliado a DevExpress de ninguna manera.
fuente
;) fácil!
fuente
Es muy simple . Cree otra propiedad para contraseña y vincúlela con TextBox
Pero todas las operaciones de entrada se realizan con la propiedad de contraseña real
cadena privada _Password;
Contraseña de cadena pública {get {return _Password; }
fuente
Bueno, mi respuesta es más simple solo para el patrón MVVM
en clase viewmodel
la propiedad de contraseña de PasswordBox que Win proporciona o WatermarkPasswordBox que XCeedtoolkit proporciona genera un RoutedEventArgs para que pueda vincularlo.
ahora en vista xmal
o
fuente
Envíe un
SecureString
al modelo de vista usando un comportamiento adjunto yICommand
No hay nada de malo con el código subyacente al implementar MVVM. MVVM es un patrón arquitectónico que tiene como objetivo separar la vista del modelo / lógica empresarial. MVVM describe cómo lograr este objetivo de forma reproducible (el patrón). No le importan los detalles de implementación, como cómo estructura o implementa la vista. Simplemente dibuja los límites y define qué es la vista, el modelo de vista y cuál es el modelo en términos de la terminología de este patrón.
MVVM no se preocupa por el lenguaje (XAML o C #) o el compilador (
partial
clases). Ser independiente del idioma es una característica obligatoria de un patrón de diseño; debe ser neutral en cuanto al idioma.Sin embargo, el código subyacente tiene algunos inconvenientes, como hacer que la lógica de la interfaz de usuario sea más difícil de entender, cuando se distribuye ampliamente entre XAML y C #. Pero la implementación más importante de la lógica de la interfaz de usuario u objetos como plantillas, estilos, disparadores, animaciones, etc. en C # es muy compleja y fea / menos legible que usar XAML. XAML es un lenguaje de marcado que utiliza etiquetas y anidamiento para visualizar la jerarquía de objetos. Crear UI usando XAML es muy conveniente. Aunque hay situaciones en las que está bien elegir implementar la lógica de la interfaz de usuario en C # (o código subyacente). Manejando el
PasswordBox
es un ejemplo.Por esta razón, manejando el
PasswordBox
código subyacente manejando elPasswordBox.PasswordChanged
, no constituye una violación del patrón MVVM.Una violación clara sería pasar un control (el
PasswordBox
) al modelo de vista. Muchas soluciones recomiendan este ejemplo, la bahía de pasar la instancia de laPasswordBox
queICommand.CommandParameter
el modelo de vista. Obviamente una recomendación muy mala e innecesaria.Si no le importa usar C #, pero solo quiere mantener limpio su archivo de código subyacente o simplemente quiere encapsular una lógica de comportamiento / UI, siempre puede hacer uso de las propiedades adjuntas e implementar un comportamiento adjunto.
Opuesto al infame ayudante de difusión amplia que permite la vinculación a la contraseña de texto sin formato (muy mal antipatrón y riesgo de seguridad), este comportamiento utiliza un
ICommand
para enviar la contraseñaSecureString
al modelo de vista, siempre que sePasswordBox
produce elPasswordBox.PasswordChanged
evento.MainWindow.xaml
ViewModel.cs
PasswordBox.cs
fuente