c# - How to bind to a PasswordBox in MVVM

ID : 11732

viewed : 327

Tags : c#wpfmvvmwpf-controlspasswordsc#





Top 5 Answer for c# - How to bind to a PasswordBox in MVVM

vote vote

95

Maybe I am missing something, but it seems like most of these solutions overcomplicate things and do away with secure practices.

This method does not violate the MVVM pattern and maintains complete security. Yes, technically it is code behind, but it is nothing more than a "special case" binding. The ViewModel still has no knowledge of the View implementation, which in my mind it does if you are trying to pass the PasswordBox in to the ViewModel.

Code Behind != Automatic MVVM violation. It all depends on what you do with it. In this case, we are just manually coding a binding, so its all considered part of the UI implementation and therefore is ok.

In the ViewModel, just a simple property. I made it "write only" since there shouldn't be a need to retrieve it from outside the ViewModel for any reason, but it doesn't have to be. Note that it is a SecureString, not just a string.

public SecureString SecurePassword { private get; set; } 

In the xaml, you set up a PasswordChanged event handler.

<PasswordBox PasswordChanged="PasswordBox_PasswordChanged"/> 

In the code behind:

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) {     if (this.DataContext != null)     { ((dynamic)this.DataContext).SecurePassword = ((PasswordBox)sender).SecurePassword; } } 

With this method, your password remains in a SecureString at all times and therefore provides maximum security. If you really don't care about security or you need the clear text password for a downstream method that requires it (note: most .NET methods that require a password also support a SecureString option, so you may not really need a clear text password even if you think you do), you can just use the Password property instead. Like this:

(ViewModel property)

public string Password { private get; set; } 

(Code behind)

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) {     if (this.DataContext != null)     { ((dynamic)this.DataContext).Password = ((PasswordBox)sender).Password; } } 

If you wanted to keep things strongly typed, you could substitute the (dynamic) cast with the interface of your ViewModel. But really, "normal" data bindings aren't strongly typed either, so its not that big a deal.

private void PasswordBox_PasswordChanged(object sender, RoutedEventArgs e) {     if (this.DataContext != null)     { ((IMyViewModel)this.DataContext).Password = ((PasswordBox)sender).Password; } } 

So best of all worlds - your password is secure, your ViewModel just has a property like any other property, and your View is self contained with no external references required.

vote vote

82

My 2 cents:

I developed once a typical login dialog (user and password boxes, plus "Ok" button) using WPF and MVVM. I solved the password binding issue by simply passing the PasswordBox control itself as a parameter to the command attached to the "Ok" button. So in the view I had:

<PasswordBox Name="txtPassword" VerticalAlignment="Top" Width="120" /> <Button Content="Ok" Command="{Binding Path=OkCommand}"    CommandParameter="{Binding ElementName=txtPassword}"/> 

And in the ViewModel, the Execute method of the attached command was as follows:

void Execute(object parameter) {     var passwordBox = parameter as PasswordBox;     var password = passwordBox.Password;     //Now go ahead and check the user name and password } 

This slightly violates the MVVM pattern since now the ViewModel knows something about how the View is implemented, but in that particular project I could afford it. Hope it is useful for someone as well.

vote vote

72

Sorry, but you're doing it wrong.

People should have the following security guideline tattooed on the inside of their eyelids:
Never keep plain text passwords in memory.

The reason the WPF/Silverlight PasswordBox doesn't expose a DP for the Password property is security related.
If WPF/Silverlight were to keep a DP for Password it would require the framework to keep the password itself unencrypted in memory. Which is considered quite a troublesome security attack vector. The PasswordBox uses encrypted memory (of sorts) and the only way to access the password is through the CLR property.

I would suggest that when accessing the PasswordBox.Password CLR property you'd refrain from placing it in any variable or as a value for any property.
Keeping your password in plain text on the client machine RAM is a security no-no.
So get rid of that public string Password { get; set; } you've got up there.

When accessing PasswordBox.Password, just get it out and ship it to the server ASAP. Don't keep the value of the password around and don't treat it as you would any other client machine text. Don't keep clear text passwords in memory.

I know this breaks the MVVM pattern, but you shouldn't ever bind to PasswordBox.Password Attached DP, store your password in the ViewModel or any other similar shenanigans.

If you're looking for an over-architected solution, here's one:
1. Create the IHavePassword interface with one method that returns the password clear text.
2. Have your UserControl implement a IHavePassword interface.
3. Register the UserControl instance with your IoC as implementing the IHavePassword interface.
4. When a server request requiring your password is taking place, call your IoC for the IHavePassword implementation and only than get the much coveted password.

Just my take on it.

-- Justin

vote vote

65

You can use this XAML:

<PasswordBox>     <i:Interaction.Triggers>         <i:EventTrigger EventName="PasswordChanged">             <i:InvokeCommandAction Command="{Binding RelativeSource={RelativeSource Mode=FindAncestor, AncestorType=PasswordBox}}" CommandParameter="{Binding ElementName=PasswordBox}"/>         </i:EventTrigger>     </i:Interaction.Triggers> </PasswordBox> 

And this command execute method:

private void ExecutePasswordChangedCommand(PasswordBox obj) {     if (obj != null)      Password = obj.Password; } 

This requires adding the System.Windows.Interactivity assembly to your project and referencing it via xmlns:i="clr-namespace:System.Windows.Interactivity;assembly=System.Windows.Interactivity".

vote vote

55

I spent a great deal of time looking at various solutions. I didn't like the decorators idea, behaviors mess up the validation UI, code behind... really?

The best one yet is to stick to a custom attached property and bind to your SecureString property in your view model. Keep it in there for as long as you can. Whenever you'll need quick access to the plain password, temporarily convert it to an unsecure string using the code below:

namespace Namespace.Extensions {     using System;     using System.Runtime.InteropServices;     using System.Security;      /// <summary>     /// Provides unsafe temporary operations on secured strings.     /// </summary>     [SuppressUnmanagedCodeSecurity]     public static class SecureStringExtensions     {         /// <summary>         /// Converts a secured string to an unsecured string.         /// </summary>         public static string ToUnsecuredString(this SecureString secureString)         {             // copy&paste from the internal System.Net.UnsafeNclNativeMethods             IntPtr bstrPtr = IntPtr.Zero;             if (secureString != null)             {                 if (secureString.Length != 0)                 {                     try                     {                         bstrPtr = Marshal.SecureStringToBSTR(secureString);                         return Marshal.PtrToStringBSTR(bstrPtr);                     }                     finally                     {                         if (bstrPtr != IntPtr.Zero)                             Marshal.ZeroFreeBSTR(bstrPtr);                     }                 }             }             return string.Empty;         }          /// <summary>         /// Copies the existing instance of a secure string into the destination, clearing the destination beforehand.         /// </summary>         public static void CopyInto(this SecureString source, SecureString destination)         {             destination.Clear();             foreach (var chr in source.ToUnsecuredString())             {                 destination.AppendChar(chr);             }         }          /// <summary>         /// Converts an unsecured string to a secured string.         /// </summary>         public static SecureString ToSecuredString(this string plainString)         {             if (string.IsNullOrEmpty(plainString))             {                 return new SecureString();             }              SecureString secure = new SecureString();             foreach (char c in plainString)             {                 secure.AppendChar(c);             }             return secure;         }     } } 

Make sure you allow the GC to collect your UI element, so resist the urge of using a static event handler for the PasswordChanged event on the PasswordBox. I also discovered an anomaly where the control wasn't updating the UI when using the SecurePassword property for setting it up, reason why I'm copying the password into Password instead.

namespace Namespace.Controls {     using System.Security;     using System.Windows;     using System.Windows.Controls;     using Namespace.Extensions;      /// <summary>     /// Creates a bindable attached property for the <see cref="PasswordBox.SecurePassword"/> property.     /// </summary>     public static class PasswordBoxHelper     {         // an attached behavior won't work due to view model validation not picking up the right control to adorn         public static readonly DependencyProperty SecurePasswordBindingProperty = DependencyProperty.RegisterAttached(             "SecurePassword",             typeof(SecureString),             typeof(PasswordBoxHelper),             new FrameworkPropertyMetadata(new SecureString(),FrameworkPropertyMetadataOptions.BindsTwoWayByDefault, AttachedPropertyValueChanged)         );          private static readonly DependencyProperty _passwordBindingMarshallerProperty = DependencyProperty.RegisterAttached(             "PasswordBindingMarshaller",             typeof(PasswordBindingMarshaller),             typeof(PasswordBoxHelper),             new PropertyMetadata()         );          public static void SetSecurePassword(PasswordBox element, SecureString secureString)         {             element.SetValue(SecurePasswordBindingProperty, secureString);         }          public static SecureString GetSecurePassword(PasswordBox element)         {             return element.GetValue(SecurePasswordBindingProperty) as SecureString;         }          private static void AttachedPropertyValueChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)         {             // we'll need to hook up to one of the element's events             // in order to allow the GC to collect the control, we'll wrap the event handler inside an object living in an attached property             // don't be tempted to use the Unloaded event as that will be fired  even when the control is still alive and well (e.g. switching tabs in a tab control)              var passwordBox = (PasswordBox)d;             var bindingMarshaller = passwordBox.GetValue(_passwordBindingMarshallerProperty) as PasswordBindingMarshaller;             if (bindingMarshaller == null)             {                 bindingMarshaller = new PasswordBindingMarshaller(passwordBox);                 passwordBox.SetValue(_passwordBindingMarshallerProperty, bindingMarshaller);             }              bindingMarshaller.UpdatePasswordBox(e.NewValue as SecureString);         }          /// <summary>         /// Encapsulated event logic         /// </summary>         private class PasswordBindingMarshaller         {             private readonly PasswordBox _passwordBox;             private bool _isMarshalling;              public PasswordBindingMarshaller(PasswordBox passwordBox)             {                 _passwordBox = passwordBox;                 _passwordBox.PasswordChanged += this.PasswordBoxPasswordChanged;             }              public void UpdatePasswordBox(SecureString newPassword)             {                 if (_isMarshalling)                 {                     return;                 }                  _isMarshalling = true;                 try                 {                     // setting up the SecuredPassword won't trigger a visual update so we'll have to use the Password property                     _passwordBox.Password = newPassword.ToUnsecuredString();                      // you may try the statement below, however the benefits are minimal security wise (you still have to extract the unsecured password for copying)                     //newPassword.CopyInto(_passwordBox.SecurePassword);                 }                 finally                 {                     _isMarshalling = false;                 }             }              private void PasswordBoxPasswordChanged(object sender, RoutedEventArgs e)             {                 // copy the password into the attached property                 if (_isMarshalling)                 {                     return;                 }                  _isMarshalling = true;                 try                 {                     SetSecurePassword(_passwordBox, _passwordBox.SecurePassword.Copy());                 }                 finally                 {                     _isMarshalling = false;                 }             }         }     } } 

And the XAML usage:

<PasswordBox controls:PasswordBoxHelper.SecurePassword="{Binding LogonPassword, UpdateSourceTrigger=PropertyChanged, ValidatesOnDataErrors=True}"> 

My property in the view model looked like this:

[RequiredSecureString] public SecureString LogonPassword {    get    {        return _logonPassword;    }    set    {        _logonPassword = value;        NotifyPropertyChanged(nameof(LogonPassword));    } } 

The RequiredSecureString is just a simple custom validator that has the following logic:

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = true)]     public class RequiredSecureStringAttribute:ValidationAttribute {     public RequiredSecureStringAttribute()         :base("Field is required")     {                 }      public override bool IsValid(object value)     {         return (value as SecureString)?.Length > 0;     } } 

Here you have it. A complete and tested pure MVVM solution.

Top 3 video Explaining c# - How to bind to a PasswordBox in MVVM







Related QUESTION?