Friday, July 25, 2014

Get Password from WPF PasswordBox with MVVM in a secure and simple way

Keeping cleartext passwords in memory is a security risk. Therefore the Password and SecurePassword property of WPF PasswordBox is not a DependencyProperty and cannot used for binding. To access the password in the ViewModel in a secure way some work have to be done.

At first we define an interface that contains just the SecureString Password as property.
public interface IHavePassword
{
    System.Security.SecureString Password { get; }
}

The View implements the IHavePassword interface. The Property Password in code-behind returns the SecurePassword of the PasswordBox.
public partial class LoginView : UserControlIHavePassword
{
    public LoginView()
    {
        InitializeComponent();
        DataContext = new LoginViewModel();
    }
 
    public System.Security.SecureString Password
    {
        get
        {
            return UserPassword.SecurePassword;
        }
    }
}

In the XAML part of the View the click event of the login button is send by using the RelayCommand pattern that builds on WPF Commanding. Therefore the Command and the CommandParameter properties are set. The View is set to the CommandParameter.
<UserControl x:Class="MvvmPassword.LoginView"
             x:Name="This"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid>
        ...
        <PasswordBox x:Name="UserPassword"
                     Grid.Row="2"
                     Grid.Column="1"
                     Margin="4" />
        
        <Button x:Name="Login"
                Grid.Row="3"
                Grid.ColumnSpan="2"
                Margin="4"
                Content="Login"
                Command="{Binding LoginCommand}"
                CommandParameter="{Binding ElementName=This}"/>
        ...
    </Grid>
</UserControl>
In the ViewModel the LoginCommand is linked to the Login method. This method gets the View as parameter. The view is casted to defined interface IHavePassword. From that we can easily obtain the secure password.
private void Login(object parameter)
{
    var passwordContainer = parameter as IHavePassword;
    if (passwordContainer != null)
    {
        var secureString = passwordContainer.Password;
        PasswordInVM = ConvertToUnsecureString(secureString);
    }
}
The SecureString needs to be converted to string to validate the entered password. There are some pitfalls of converting a SecureString. A nice explanation of them can be found in How to properly convert SecureString to String by Fabio Pintos.
private string ConvertToUnsecureString(SecureString securePassword)
{
   if (securePassword == null)
   {
     return string.Empty;
   }
 
   IntPtr unmanagedString = IntPtr.Zero;
   try
   {
     unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(securePassword);
     return Marshal.PtrToStringUni(unmanagedString);
   }
   finally
   {
     Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
   }
}

No comments:

Post a Comment