PasswordBox implementation using MVVM is one of the widly debated topic around MVVM arena. Problem statement is how to bind Password property of PasswordBox to a property in a ViewModel. This problem stems from the fact that Password property of the PasswordBox is not a dependency property for obvious security reasons, hence we cant bind it as we would normally do.
Now there are many solutions and workarounds like having event in ViewModel that is registerd in view and acts as the password catcher, passing whole PasswordBox to ViewModel in command paramter of login button Command, having content control binding to the property in ViewModel of type PasswordBox, handling password change in code behind and creating PasswordBoxHelper attached behaviour etc.
You can find the discussion and solutions in below thread :-
http://stackoverflow.com/questions/1483892/how-to-bind-to-a-passwordbox-in-mvvm
What I found well suited in accordance with the MVVM implementation is having attached property extend the PasswordBox and provide much needed dependency property for binding. Below is sample of PasswordHelper class that extends the PasswordBox and provide dependency property BindablePassword for binding.
Sample is based on below blog post
http://blog.functionalfun.net/2008/06/wpf-passwordbox-and-data-binding.html
I have modified the PasswordHelper class to incorporate following :-
1) Implementation of SecureString type for dependency property instead of String.
2) Binding SecureString type property from ViewModel to PasswordBox's SecurePassword property instead of Password property.
3) Utility method to convert from SecureString to String and vice versa.
These all have been implemented to tackle the security issues with implementing dependency property as String. Which I will shortly demonstrate.
PasswordHelper
public static class PasswordHelper
{
public static readonly DependencyProperty BindablePasswordProperty =
DependencyProperty.RegisterAttached("BindablePassword",
typeof(SecureString), typeof(PasswordHelper),
new FrameworkPropertyMetadata(string.Empty, OnPasswordPropertyChanged));
public static readonly DependencyProperty BindPasswordProperty =
DependencyProperty.RegisterAttached("BindPassword",
typeof(bool), typeof(PasswordHelper), new PropertyMetadata(false, BindPassword));
private static readonly DependencyProperty UpdatingPasswordProperty =
DependencyProperty.RegisterAttached("UpdatingPassword", typeof(bool),
typeof(PasswordHelper));
public static void SetBindPassword(DependencyObject dp, bool value)
{
dp.SetValue(BindPasswordProperty, value);
}
public static bool GetBindPassword(DependencyObject dp)
{
return (bool)dp.GetValue(BindPasswordProperty);
}
public static string GetBindablePassword(DependencyObject dp)
{
return (string)dp.GetValue(BindablePasswordProperty);
}
public static void SetBindablePassword(DependencyObject dp, SecureString value)
{
dp.SetValue(BindablePasswordProperty, value);
}
private static bool GetUpdatingPassword(DependencyObject dp)
{
return (bool)dp.GetValue(UpdatingPasswordProperty);
}
private static void SetUpdatingPassword(DependencyObject dp, bool value)
{
dp.SetValue(UpdatingPasswordProperty, value);
}
private static void OnPasswordPropertyChanged(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
passwordBox.PasswordChanged -= PasswordChanged;
if (!(bool)GetUpdatingPassword(passwordBox))
{
passwordBox.Password = (string)e.NewValue;
}
passwordBox.PasswordChanged += PasswordChanged;
}
private static void BindPassword(DependencyObject sender,
DependencyPropertyChangedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
if (passwordBox == null)
return;
if ((bool)e.OldValue)
{
passwordBox.PasswordChanged -= PasswordChanged;
}
if ((bool)e.NewValue)
{
passwordBox.PasswordChanged += PasswordChanged;
}
}
private static void PasswordChanged(object sender, RoutedEventArgs e)
{
PasswordBox passwordBox = sender as PasswordBox;
SetUpdatingPassword(passwordBox, true);
SetBindablePassword(passwordBox, passwordBox.SecurePassword);
SetUpdatingPassword(passwordBox, false);
}
}
View Model
public class MainWindowViewModel
{
SecureString networkPassword;
public SecureString NetworkPassword
{
get
{
return networkPassword;
}
set
{
networkPassword = value;
}
}
public MainWindowViewModel()
{
//Just for demo
NetworkPassword = SecureStringUtility.ConvertToSecureString("SomeString");
}
}
View
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:PBinding"
Title="MainWindow" Height="350" Width="525">
Height="80"
Margin="2" />
Sample is simple containing window with PasswordBox control, a ViewModel with single property NetworkPassword to bind to PasswordBox, PasswordHelper class to extend PasswordBox and Utility methods to convert from SecureString to String and vice versa.
Let come to the reason for implementing SecureString in PasswordHelper class. If we go by the orignal sample PasswordAssistant class from above blog which is implementing String as dependency property, we would get into some security risk as below screenshot shows. Password can be easily reterived using WPF sniffer tool like Snoop which I have used.
So as we can see Password in password field of PasswordAssistant class is easily visible if Dependency Property is of Type String in PasswordAssistant class.
Now below is the screen shot of same application using PasswordHelper class with SecureString implementation.
SecureString is type from System.Security namespace and PasswordBox in WPF also contains property SecurePassword along with Password property. These both properties are not Dependency properties so can't be used in binding. For that reason we have to come up with Attached dependency property for PasswordBox with with we can bind our ViewModel's property. Now property in ViewModel should also be SecureString type as we have to bind it with SecureString type dependency property.
So in our application,Password remains as secure string and can be converted from Secure string to string and vice versa wherever needed. This can be done using utility methods ConvertToUnsecureString and ConvertToSecureString.
public static string ConvertToUnsecureString(SecureString securePassword)
{
if (securePassword == null)
throw new ArgumentNullException("securePassword");
IntPtr unmanagedString = IntPtr.Zero;
try
{
unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
return Marshal.PtrToStringUni(unmanagedString);
}
finally
{
Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
}
}
public static SecureString ConvertToSecureString( string password)
{
if (password == null)
throw new ArgumentNullException("password");
unsafe
{
fixed (char* passwordChars = password)
{
var securePassword = new SecureString(passwordChars, password.Length);
securePassword.MakeReadOnly();
return securePassword;
}
}
These methods proper implementation has been taken from the blog :-
http://blogs.msdn.com/b/fpintos/archive/2009/06/12/how-to-properly-convert-securestring-to-string.aspx
We have just modified the PasswordHelper class to provide secure and proper MVVM based implementation for PasswordBox. Hope it will be helpful.