Click here to Skip to main content
Click here to Skip to main content

Passwords in WPF: How to find yours and how to prevent it being found

By , 23 Nov 2008
Rate this:
Please Sign up or sign in to vote.

Introduction

Windows Presentation Foundation introduces a new control for entering passwords, but unfortunately, like the TextBox of Win32, it is not too complicated to reveal the magic word hidden under the asterisks.

The fastest method is to analyze the VisualTree (the tree view of controls) with Snoop and go directly to read the password control.

Unsecure_WPF_LogOn.jpg

In seconds, the password can be found, but we can make life more difficult for prying eyes with a few lines of code.

First, how can we hide a value of a property from Snoop?

Snoop displays both the dependency property and the CLR property, but we will create a dependency property specifically to trick the program (at least in the current version).

Create a simple Window in a new WPF project called MainWindow and to insert a normal DependencyProperty called MyString1 with an easily recognizable default value:

public partial class MainWindow : Window
{
   public MainWindow()
   {
      InitializeComponent();
   }
   public static readonly DependencyProperty MyString1Property = 
      DependencyProperty.Register(
      "MyString1",
      typeof(string),
      typeof(MainWindow),
      new FrameworkPropertyMetadata("default value of MyString1")
   );
   public string MyString1
   {
      get { return (string)GetValue(MyString1Property); }
      set { SetValue(MyString1Property, value); }
   }
}

Snooping the MainWindow (jargon that means opening Snoop, selecting our running application, clicking "Snoop this application", and looking in the tree view element MainWindow), we can see and change the default value MyString1. Add this code to the constructor of the class:

MyString1 = "nuovo valore";

The new value is shown by Snoop on Window load, and everything works properly.

We now create a new DependencyProperty called MyString2, inserting the code for a normal CLR property in the CLR "wrapper" code:

public static readonly DependencyProperty MyString2Property = 
   DependencyProperty.Register(
   "MyString2",
   typeof(string),
   typeof(MainWindow),
   new FrameworkPropertyMetadata("default value of MyString2")
);

string _myString2 = "value of internal field of MyString2";
public string MyString2
{
   get { return _myString2; }
   set { _myString2 = value; }
}

How will Snoop behave now with MyString2? Simple, it will continue to read the DependencyProperty managed by the WPF property system while "the code" will use the CLR wrapper. In other words, Snoop reads MyString2 with the method GetValue, while the code reads MyString2 through _myString2.

Perfect, we found how to get away from Snoop!

To better exemplify the enclosed source code AntiSnoopSample, a small program that I wrote to show the potential of a property dependency "badly written", I include a CLR property MyString3 to show that Snoop correctly reads the property dependency.

With what we have learned, let’s get back to our problem, analyzing the control PasswordBox; we notice immediately that it is marked with sealed (can not be inherited). This excludes a direct derivation of our control that stores the password in a field not easily readable by Snoop .

Object_Browser_PasswordBox.jpg

At this point, we can choose two roads: derive "our PasswordBox" from a TextBox control, or try to work around this limitation by managing the PasswordChanged event.

The event PasswordChanged is triggered when the property value is changed. Then, we can copy the character inserted in a private field to our window, and replace the password of PasswordBox with asterisks (in the attached code, the password is replaced with asterisks). Now, we try to recover our password with Snoop: we only get asterisks. In our application, we should use the private field (in the code, this.pwd) to use the clear password.

Snoop_secure_WPF_LogOn.jpg

This choice has several limitations. Because the control PasswordBox is "closed", it is not possible to detect the position of the cursor text. It is not possible to replicate the password in private if it was a pasted text or if you press the Delete key or Backspace.

We will have to simply delete the entire password under any of these conditions. But in some scenarios, this could go well.

If we choose the path of derivation instead, we have greater flexibility, but also greater "responsibility": the code will have to locate the position of changes in the password and reproduce it in a field not visible in Snoop.

I implemented the control SecurePasswordBox derived from a TextBox for a illustration. The whole logic is executed in the override of the OnTextChanged event and a clear password is recoverable through the usual Text property of the control. Snoop does not read the Text property of their own SecurePasswordBox because, as explained, its CLR wrapper implements a normal private field instead of calling SetValue / GetValue.

Snoop calling GetValue effectively reads "base.Text" or the text contained in the inherited control but set to a series of asterisks, and our privacy is safe from the malicious use of Snoop. Mission accomplished.

NB: These solutions are not be definitive, a modified version of Snoop could very well read passwords even in these cases, or the attacker could analyze the memory of the program and find the password in each case. But, these solutions will defend the security of the application.

The advice is not to allow the arbitrary execution of any program in Full Trust in sensitive environments to avoid unpleasant surprises.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Leonardo T.
Other
Italy Italy
Software Researcher at the University of Parma (Italy)

Comments and Discussions

 
QuestionQuestion Regarding Code PinmemberTaylor Leese26-Nov-09 2:12 
Hello,
 
I came across your example today and I'm trying to understand one thing in the setter for the code below. I attempted to change one line just trying to investigate what would happen and by setting base.Text = value rather than to the string of masked characters it will then show the unmasked string in the text box. I don't understand why because I can see OnTextChanged being called after setting base.Text and I can see this.BaseText being set again in OnTextChanged to the string of '*' characters. However, the GUI still shows the original unmasked string. Any thoughts on why?
 
        public new string Text
        {
            get { return _password; }
            set
            {
                _password = value;
                //this.BaseText = new string(PWD_CHAR, value.Length);
                base.Text = value;
            }
        }
 
Thank you,
Taylor
GeneralMy vote of 5 PinmemberDr.Luiji18-Dec-08 9:14 
GeneralComments PinmemberGreizzerland25-Nov-08 5:48 
GeneralMy vote of 1 Pinmemberxliqz24-Nov-08 6:16 
QuestionWhat about using something like a substitution cipher? Pinmembersupercat924-Nov-08 5:53 
GeneralRe: What about using something like a substitution cipher? Pinmembersupercat924-Nov-08 8:31 
GeneralMy vote of 1 PinmemberSimon Stevens24-Nov-08 5:45 
General[Message Deleted] Pinmemberpsouza4micronet24-Nov-08 3:37 
GeneralUseless - my vote of 1 PinmemberMarco Mastropaolo24-Nov-08 2:52 
GeneralMy vote of 1 PinmemberBartosz Wojcik24-Nov-08 1:51 
GeneralThe article name should be how to protect password from Snoop PinmemberShivprasad koirala24-Nov-08 0:22 
GeneralSorry, doesn't really serve a purpose PinmemberWilliam E. Kempf21-Nov-08 4:31 
GeneralRe: Sorry, doesn't really serve a purpose PinmemberYogesh Jagota23-Nov-08 23:28 
GeneralVery good PinmemberPaul Conrad20-Nov-08 9:43 
GeneralRe: Very good PinmemberPaul Conrad20-Nov-08 10:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web02 | 2.8.140415.2 | Last Updated 24 Nov 2008
Article Copyright 2008 by Leonardo T.
Everything else Copyright © CodeProject, 1999-2014
Terms of Use
Layout: fixed | fluid