
Introduction
Have you ever taken a good look at that TextBox
control you are using to let users enter a password? Have you wondered why you can't get the TextBox
to show the black dots instead of * as a password is entered?
This article presents the PasswordBox
control and attempts to address the issues surrounding the use of a TextBox
control for input of a password or other secure data.
Background
I was happily coding away on the options dialog for my latest project and was ready to test the dialog. When I ran my app and opened the dialog I noticed something wasn't right. All the controls had the XP visual styles (I was using a manifest). That is, they all had the visual styles except one...The password field! It displayed *'s instead of the black dots one has come to expect.
I searched high and low, on MSDN and the rest of the internet (yes, including The Code Project)...I found nothing to help.
I asked in a Microsoft newsgroup...The only answer I got involved assigning UNICODE character 25CF
to PasswordChar
.
textBox1.PasswordChar = (char)0x25CF;
I wasn't happy with this solution [read "hack"] since it wouldn't work on all systems. So I started doing more research. What I found surprised me.
I started up Outlook Express (OE) and fired up SPY++ to have a look at the password field in the account properties dialog. When I compared the password field in OE with the TextBox
control I found that OE uses the ES_PASSWORD
style flag. Now, one may think that by assigning "*" to the PasswordChar member the TextBox
control would then use this this style also...Nope! (OK, I should have remembered ES_PASSWORD
from coding dialogs in VC++)
While inspecting the .NET TextBox
with SPY++ I could see the password that was entered! Could it be there is this security hole? I checked, I COULD NOT see the password entered into OE.
How easy would it be for a malicious hacker to write a something to read your passwords from a password field? Dead easy!
As I see it now there are at least 2 major issues surrounding the use of the TextBox
control for input of a password. I will now address both of these issues individually, in no particular order of importance.
Where are the black dots?
As it turns out, this was the simplest issue to resolve.
I originally set out to use P/Invoke to call GetWindowsLong
& SetWindowsLong
to try to modify the style of the TextBox
and add the ES_PASSWORD
style flag. While searching for the documentation on this I came across the CreateParams
member of the Form class.
I won't go into great detail on this member since you can look it up yourself in the online help. Suffice it to say, for those familiar with MFC development, I liken CreateParams
to the PreCreateWindow
function.
Getting the black dots to show up based on the visual style was insanely simple!
public class PasswordBox : TextBox
{
private const int ES_PASSWORD = 0x0020;
...
protected override CreateParams CreateParams
{
CreateParams cp = base.CreateParams;
...
cp.Style |= ES_PASSWORD;
...
return cp;
}
}
This solved the issue of the black dots. And should work on any system supporting the .NET Framework. If it doesn't, I'm sure one of you will let me know. Just don't be too rough on me :)
And what about security?
It was at this point I thought I had my solution. I couldn't have been more wrong...WRONG!.
I discovered that using the ES_PASSWORD
style only created what I needed visually. I could still see the password using SPY++. This was going to take a little more work.
To hide a password from prying eyes I found that I needed to define a private internal buffer.
...
private System.Text.StringBuilder _internalBuffer
= new System.Text.StringBuilder();
...
In order to get the keyboard input into my buffer I had to capture keystrokes.
At first I used KeyDown
and KeyPress
event handlers. The code for editing the internal buffer while feeding '\0x20' to the base TextBox
class was cumbersome and fairly effective, but, it had some problems.
The most noticeable problem was pasting text from the clipboard. The keyboard events just didn't provide the means to capture the keystrokes and handle them myself. So, I resigned myself to the fact that I was going to have to override WndProc
and do all this by hand.
While researching how to do this in C# I stumbled across 2 methods:
protected virtual bool ProcessCmdKey(ref Message msg, Keys keyData);
protected internal virtual bool ProcessKeyMessage(ref Message m);
Again, I won't go into any detail on the usage of these methods, since you can read all about them in your online help or on MSDN. I overrode these 2 methods and all my problems were solved!
ProcessCmdKey
allowed me to capture Ctrl+V and the Delete key to handle a paste and delete operations the way I needed. And ProcessKeyMessage
let me to process each the WM_CHAR
message so that I could grab the character being input put it into my buffer and send '\0x20' back through the message loop
Using the Code
Using the code couldn't be simpler. Your first option is to build the control project then add the component to you tool bar. Once on your toolbar simply drag and drop the control onto your form. From there just treat it like any TextBox
control.
The next option is to add build the control project and add it to your references of your own project. In your Form code add a declaration for PasswordBox
. And again, treat it like any other TextBox
control.
public class MyLogin : Form
{
...
private PasswordBox _password;
...
public MyLogin()
{
_password = new PasswordBox();
...
}
...
}
You last option is less scalable, but you could simply copy the PasswordBox.cs file to your own project and use it as in the previous option.
Note: You can still assign a value to PasswordChar
, such as '*', but be aware, you will get that character instead of the black dots, regardless of the visual style in use.
Points of Interest
I found a couple of interesting [read strange] things. Take a look at a TextBox
with SPY++, you will see that the WS_MAXIMIZEBOX
style is added to the control. What's that all about? I haven't figured that out. In the source I have included the code you need to remove the style if you choose. I haven't noticed any adverse affects when it is removed, but chose to leave it alone. You are welcome to try on your own.
The other thing I found was an issue with the ES_PASSWORD
style. I wrote and experimented with this code for a few days. Each time I ran the code I would check the style flags with SPY++ and each time I would see the ES_PASSWORD
style flag. Then at some point I had to restart VS.NET for some reason. From then on, every time I inspected my PasswordBox
, I have not seen the ES_PASSWORD
style flag. Although the PasswordBox
does still behave as if the style flag were set. Maybe you will have different results.
Room for Improvement
As with anything, there is always room for improvement. In the case of PasswordBox
there is one thing that comes to mind, the balloon help that pops up when you try to copy the text from a password field.
When using using a TextBox
with PasswordChar='*'
and you try to copy the text, you get a balloon help that says, "Not Allowed", "You cannot copy text from a password field". With PasswordBox there is no balloon help.
History
03/16/2003 - Initial release