2

Is there any way to make the PasswordBox in a WPF application work like the one used in most phone or tablet applications. That is to say, have it show the last character entered in plaintext for a brief period.

hcham1
  • 1,799
  • 2
  • 16
  • 27

1 Answers1

2

There is no way built in to do this since it's breaking security guidelines. Please see this post for more information on why this isn't recommended: How to bind to a PasswordBox in MVVM

Here is the accepted answer:

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.

If you still want to achieve this, I was able to do so using a TextBox control.

XAML:

 <TextBox Name="tbPassword"/>

Codebehind:

  string actualPassword = "";
  string displayedPassword = "";
  DispatcherTimer dispatcherTimer = new DispatcherTimer();

  public MainWindow()
  {
     InitializeComponent();
     tbPassword.PreviewKeyDown += tbPassword_PreviewKeyDown;
     tbPassword.PreviewTextInput += tbPassword_PreviewTextInput;

     dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
     dispatcherTimer.Interval = new TimeSpan(0, 0, 1);
  }

  private void tbPassword_PreviewKeyDown(object sender, KeyEventArgs e)
  {
     if (e.Key == Key.Back)
     {
        if (actualPassword.Length > 0)
        {
           actualPassword = actualPassword.Substring(0, actualPassword.Length - 1);
           if (actualPassword.Length > 0)
           {
              ShowLastCharacter();
              tbPassword.CaretIndex = displayedPassword.Length;
           }
        }
     }
  }

  private void tbPassword_PreviewTextInput(object sender, TextCompositionEventArgs e)
  {
     actualPassword += e.Text;
     e.Handled = true;
     ShowLastCharacter();
     tbPassword.CaretIndex = displayedPassword.Length;
  }


  private void ShowLastCharacter()
  {
     var lastChar = actualPassword.Substring(actualPassword.Length - 1);
     displayedPassword = "";
     for (int i = 0; i < actualPassword.Length - 1; i++)
        displayedPassword += "•";
     displayedPassword += lastChar;
     tbPassword.Text = displayedPassword;

     if (dispatcherTimer.IsEnabled)
        dispatcherTimer.Stop();

     dispatcherTimer.Start();
  }

  private void dispatcherTimer_Tick(object sender, EventArgs e)
  {
     displayedPassword = "";
     for (int i = 0; i < actualPassword.Length; i++)
        displayedPassword += "•";

     tbPassword.Text = displayedPassword;
     tbPassword.CaretIndex = displayedPassword.Length;
  }
Community
  • 1
  • 1
hcham1
  • 1,799
  • 2
  • 16
  • 27
  • I saw that article about generalized databinding and suspected it might have been why I couldn't find a built in option to enable it. In my case the customer wants to offer a short numeric pin as an 'honor system' alternative to more secure authentication options (eg smart cards) to their end users. Assuming they're adamant about including it, a keyspace small enough to trivially bruteforce even if stored using a slow hash is an opening so large it arguably trivializes this sort of in-memory attack. *sigh* – Dan Is Fiddling By Firelight Sep 29 '16 at 13:33