Almost all Windows applications dealing somehow with passwords are using the standard Windows edit controls with the password flag set. Unfortunately, these controls aren't that secure by default. This article is an attempt to improve its security.
In Windows 95 and 98, applications were able to send a
WM_GETTEXT message to the edit controls to get the password, just like getting the text out of a normal edit control. This itself isn't bad, but Windows allowed you to send
WM_GETTEXT messages to other applications, too. You just sent such a message to all other windows and easily get all passwords.
In Windows NT/2000/XP, this bug has been fixed. You cannot use
WM_GETTEXT messages any more to get the passwords out of other applications (still working when the application owning the control sends the message). But some people developed new methods to get the sensitive texts. One approach for example is injecting some code into the target process using Windows hooks. The injected code is in the process memory of the target application then and is able to call the
WM_GETTEXT messages. The code then can do whatever it wants with the password. For implementation details of this method, see . So, the password edit controls in Windows NT/2000/XP aren't that secure either.
In my previous article , I've solved this problem already. Now, the
CSecureEditEx control goes one step further: process memory protection.
The Windows standard edit controls and my previous
CSecureEdit control store the passwords as plain-text in the process memory. My previous class did protect the window of the control:
WM_GETTEXT messages didn't work. Anyway, even the improved control stored the password plainly in the memory.
If Windows caches your process into the swap file, you're out of luck with these controls: the password is written plainly to the swap file, possibly lying around there for days. Of course, it's not a simple task for an attacker to find the password in the swap file, but why not making it even harder for him? The new
CSecureEditEx control does this: even if Windows caches your process to disk, the passwords aren't visible in plain-text.
You'll see that the
CSecureEditEx control not only introduces the new process memory protection, but also some limitations. That's the main reason why I'm posting a new article and not just updating the
Features of the
- None of the many available window spies are able to read the entered password.
- Removing the
ES_PASSWORD style doesn't affect
CSecureEditEx controls look like normal Windows edit controls.
- User can insert a character anywhere (i.e. use cursor keys).
- User can delete characters.
- User can paste text into
- User cannot copy text out of
- Pressing Shift-Home or Shift-End clears the contents of the control.
- Very easy to implement and use.
- Selections aren't possible. The user cannot select character ranges in the edit control.
CString DDX functions to the control won't work any more.
The selection limitation is a natural consequence of the internal processing of keys and edit control changes. If selections would be possible, the control couldn't decide which part of the text has changed or deleted.
But since we're making password edit controls, selections aren't that required anyway. Users normally delete the whole entered text when they notice that they typed some wrong key. For this, most people use Shift-Home to select the whole text. Exactly this combination clears the whole control, so there aren't any real usability disadvantages.
CSecureEditEx control is pretty simple. Just follow these steps:
- Incorporate the SecureEditEx.cpp and SecureEditEx.h files into your project.
#include the SecureEditEx.h in the header file of your dialogs/views/etc. which should use the new control.
- Replace the
CEdit variable of the control by
- Make sure your password edit control has the
ES_PASSWORD set (in resource editor).
Done! That's all you have to do! No additional initialization or something like this is needed.
The last step is optional. It won't lower security if you don't set this flag, but some context menu operations like copying the text are possible then (they won't work, therefore it would be just nice to show those items grayed). I recommend enabling the
Now, how do you get the entered password out of the control, if the
WM_GETTEXT message doesn't work any more? DDX to
CStrings doesn't work any more, too. You have to use the following member function:
This returns the entered text as a
LPTSTR pointer. Be very careful what you are doing with it! Don't just copy it to some other location, the whole security improvement would be lost then! You are of course allowed, for example, to hash the text with a one-way hash function (SHA-1) and store the hash.
When you've processed the password, you need to free the pointer, i.e. securely erase and free the memory! Use the following member function to accomplish this:
void CSecureEditEx::DeletePassword(LPTSTR lpPassword)
LPTSTR pointer returned by the
GetPassword() function to this function. It will securely erase and free the memory. The
lpPassword pointer then is invalid after the execution of the function.
Mostly you will want to set a password as default. The
WM_SETTEXT message doesn't work, you need to call the following function in order to set the current password:
void CSecureEditEx::SetPassword(LPCTSTR lpPassword)
This sets the current password to the string
lpPassword points to. If you pass
NULL as parameter, the edit control is cleared (i.e. simply no text).
CSecureEditEx internally keeps an array of pointers to single
TCHARs are XORed with some fixed character that is generated randomly in the constructor of the class. So, the characters aren't just spreaded over the process memory, they are additionally XORed with some random character.
When the user presses a key, the control looks what has changed.
If the contents of the control are shorter than before, the user has deleted something. In this case, it gets the current cursor position and calculates how many characters have been deleted. It then frees the internal memory pointers (first sets the single characters to 0) and removes them from the pointers list.
If there are more characters than before, those characters are extracted and added: first allocate the memory for the characters, XOR them with the appropriate value and insert the pointer to this location to the pointers list.
As you can see, the control uses the cursor position to see what happened. In order to get this working correctly, the user must not select any range of characters. If he would do this, we couldn't determine the operation and changed characters correctly. Therefore, the control prevents all selecting methods (selecting using Shift and cursor keys, selecting using the mouse, etc.)
To clear the whole control, the user can just press Shift-Home or Shift-End. This clears the control.
- Brian Friesen: PasswordSpy - Retrieving lost passwords using Windows hooks.
- Dominik Reichl: Secure Edit Controls - Secure Edit controls that are resistant to password revealers.
- 2005-04-16: v1.0