» Download demo project - 72 Kb
» Download source - 18 Kb
Index:
» Introduction
» Features and Limitations
» Using the Control
» Technical Background / How It's Working
» References
» Version History
Introduction
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 got 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 [1]. So, the password edit controls in Windows NT/2000/XP aren't that
secure either.
In my previous article
[2],
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 CSecureEdit control.
Features and Limitations
Features of the CSecureEditEx control:
- None of the many available window spies are able to read the entered password.
- Removing the
ES_PASSWORD style doesn't affect CSecureEditEx controls.
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
CSecureEditEx controls.
- User cannot copy text out of
CSecureEditExs.
- Pressing Shift-Home or Shift-End clears the contents of the control.
- Very easy to implement and use.
Limitations:
- Selections aren't possible. The user cannot select character ranges in the
edit control.
- Your
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.
Using the Control
Using the 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 CSecureEditEx.
- 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 ES_PASSWORD
style.
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:
LPTSTR CSecureEditEx::GetPassword()
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)
Pass the 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).
Technical Background / How It's Working
The CSecureEditEx internally keeps an array of pointers to single
TCHARs. These 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.
References
[1] Brian Friesen:
PasswordSpy - Retrieving lost passwords using Windows hooks.
[2] Dominik Reichl: Secure Edit Controls -
Secure Edit controls that are resistant to password revealers.
Version History
- 2005-04-16: v1.0
Initial release.