Every time you need to store sensitive data your first
thought use to be encryption. You probably gather that data from the store, decrypt it
and put it on a managed string in order to be consumed by
some API (ie. ConnectionStrings properties and the like).
However you should know that this kind of pattern is
susceptible to disclosure by anyone who can read your
process memory since your data is not encrypted anymore.
Also, this same scenario will be seen whenever you process
gets swapped out to disk, so all your unencrypted data will
be placed in your swap file. As you may already know, the
standard System.String is by no means intended to store
sensitive data for several reasons, most of them listed in
this
Shawn's
post. The most important of these reasons is the
immutability of the managed string object.The recommended solution before Whidbey (a.k.a VS 2005) was
to use a byte array. Since byte arrays can be pinned down,
encrypted, and zeroed out, many of the above concerns were
solved. Whidbey will be introducing a new class,
SecureString, which helps to make this all easier for you.
This class will address all the mentioned issues by holding
an encrypted version of your data (using
DPAPI), and will be only unencrypted when accessed (later we‘ll
see how).And now let’s see the good part. If you don’t want to wait
till Whidbey RTM appears, I developed a similar class (same
public interface) that will mimics the SecureString version
in Whidbey but for .NET v1.1. However you should know that
like its Whidbey counterpart, since SecureString uses DPAPI,
you should need the required service packs (usually better
than sp3) on Windows 98, ME and Windows 2000.
Downloads
You can download the sample usage along with the NCrypto
cryptography library from the
Sourceforge
site. The specific
example is under the “TestSecureString” folder. You will
find many other projects that test the rest of the NCrypto
classes.
SecureString Description
Since the internal implementation uses a pinned byte array,
the garbage collector will not move the encrypted string
around in memory and you will be able to destroy and release
this memory by using the implemented IDisposable interface.
This class also provides a feature that lets you lock the
internal data down as read only, preventing other code from
modifying your string.The class provides two constructors. The default one will
build be constructed without an existing character array,
and data can be copied in one character at a time. The other
constructor will receive a pointer to a character array, and
the length of that array. When constructed this way, the
class will make a copy of your array, allowing you to zero
out your insecure copy. Whether you create a SecureString instance from a char
pointer or just a new empty one, you may add or modify data
in your string with the following methods. For instance,
you may add one char at a time with “AppendChar()”, insert
new chars with “InsertAt()”, remove at the specified
position with “RemoveAt()”, and modify at certain position
with “SetAt()” method as well as a Length property. You may
also lock down your string with “MakeReadOnly()” and
IsReadOnly() methods. Finally, you will destroy every trace
of your string with Clear(), Dispose(), and the
finalizer.
The Sample
One of the most common scenarios for this class will be the
credentials gathering from the UI and later use by the
application. The following example will show the use of
UICredentialsHelper class of the NCrypto library mentioned
above. This class provide access to a feature provided by
Windows XP and Windows Server 2003 called "Stored User Names
and Passwords" to associate a set of credentials with a
single Windows user account, storing those credentials using
the Data Protection API (DPAPI). Using these APIs will
provide you with a consistent user interface and will allow
you to automatically support the caching of these
credentials by the operating system. The “PromptForSecureCredentials” method returns an instance of the “SecureCredential” class that will hold, as you might guess, the password
data inside a SecureString. The next figure shows a very
simple usage of this class.
|
using( SecureCredential credentials =
UICredentialsHelper.PromptForSecureCredentials(
"SecureCredentials", "Enter your credentials" ) ){ if( credentials ==
null ) { return; } // Make some use of these credentials. // ...}
|
When the “PromptForSecureCredentials” method is called, you will see a dialog like the one in
the figure that will prompt you for your credentials, that
is your username and password. The password will never be
stored in a managed string so you will have complete
control of this sensitive data when in transit (memory
volatile store). The option to remember your password will
be safely stored by the OS using (you guess it) DAPI.
SecureString to Managed String
Getting data out of the SecureString is not as
straightforward as you may image at first glance but it
can be done with the help of the some static
methods. These methods are the same that you will find
in the Marshal class of Whidbey and has been extended to
provide methods that convert a SecureString into a BSTR
or a raw block of ANSI or Unicode memory. However we
don’t have these methods in v1.1 so I added some of
these methods with the same signature to my version of
SecureString. In this figure you have a sample usage of
“SecureStringToGlobalAllocUni” that will return a pointer to the Unicode unencrypted
data in memory. Once you're done using the unprotected
string, you need to make sure to erase that copy. You
can do that with the help of “ZeroFreeGlobalAllocUni” method.
|
IntPtr ustr =
SecureString.SecureStringToGlobalAllocUni(
credentials.Password ); try{ // WARNING: This managed string will remain in
memory until // collected by the GC. string
clearTextPwd = Marshal.PtrToStringUni( ustr ); Console.WriteLine( "Pwd: {0}", clearTextPwd );}finally{ // Once you're done using the unprotected string,
you need // to make sure to erase that copy. SecureString.ZeroFreeGlobalAllocUni( ustr );}
|
As Shawn posted in his blog, SecureString will only
continue to become more useful as more and more APIs begin
to use it. An interesting use in Whidbey was posted
here. In an ideal world, eventually you'd be able to go
end-to-end with your SecureString never being converted to a
standard String. However this is not possible with much of
the current APIs out there (like the ConnectionString sample
mentioned above).
It's also important to
remember that you won't necessarily have to convert to a
String like the last sample showed. For instance, if the
final use of the string is in native code, than it can be
passed through without ever becoming a managed string and
hitting the problem where the GC can move it about in
memory.Hopefully this samples and classes will give you a good
idea of how to handle sensitive data that is usually packed
in a typical managed string. Enjoy it! This posting is provided "AS IS" with no warranties, and
confers no rights.