You are on page 1of 3

Making Strings More Secure

27 May 2004 12:26 PM

http://blogs.msdn.com

The standard System.String has never been a very secure solution for storing sensitive strings
such as passwords or credit card numbers. Using a string for this purpose has numerous
problems, including:

 It's not pinned, so the garbage collector can move it around at will leaving several copies
in memory
 It's not encrypted, so anyone who can read your process' memory will be able to see the
value of the string easily. Also, if your process gets swapped out to disk, the unencrypted
contents of the string will be sitting in your swap file.
 It's not mutable, so whenever you need to modify it, there will be the old version and the
new version both in memory
 Since it's not mutable, there's no effective way to clear it out when you're done using it

Before Whidbey, the recommended solution 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, that helps to make this all easier for you.

SecureStrings are held in encrypted memory by the CLR (using DPAPI), and are only
unencrypted when they are accessed. This limits the amount of time that your string is in
plaintext for an attacker to see. Since SecureString uses DPAPI to help secure your data, it's not
available on Windows 98, ME, or Windows 2000 with anything less than service pack 3.

The garbage collector will not move the encrypted string around in memory, so you never have
to worry about multiple copies of your string sitting in your address space (unless you make
those copies). SecureString also implements IDisposable, and when it's disposed (or finalized if
you forget to dispose it), the memory that was used to hold your encrypted string will be zeroed
out (several times actually). They also provide a feature that lets you lock them down as read
only, preventing other code from modifying your string.

You can create a SecureString with a pointer to a character array, and the length of that
array. When constructed this way, the SecureString will make a copy of your array, allowing
you to zero out your insecure copy. A SecureString can also be constructed without an existing
character array, and data can be copied in one character at a time.

In order to add data to or modify data in your string, standard operations are provided. For
instance, you'll find AppendChar(), InsertAt(), RemoveAt(), and SetAt() methods as well as a
Length property. MakeReadOnly() and IsReadOnly() allow you to lock down the
SecureString. Clear(), Dispose(), and the finalizer take care of removing any trace of the
SecureString from memory.
Here's a sample method that will read a SecureString out of the console. Since the ConsoleKey
objects have no way to be cleared, there's still the possibility that an attacker could get at this
string, either by finding the ConsoleKey's sitting on the stack, having access to the stream behind
Console.In before it gets overwritten. However, reading into a SecureString helps to minimize
the overall attack surface area. Whenever possible, as you're reading data into the secure string,
you'll want to make sure that you erase the character that you just added.

/// <summary>
/// Read a password from the console into a SecureString
/// </summary>
/// <returns>Password stored in a secure string</returns>
public static SecureString GetPassword()
{
SecureString password = new SecureString();

// get the first character of the password


ConsoleKeyInfo nextKey = Console.ReadKey(true);

while(nextKey.Key != ConsoleKey.Enter)
{
if(nextKey.Key == ConsoleKey.BackSpace)
{
if(password.Length > 0)
{
password.RemoveAt(password.Length - 1);

// erase the last * as well


Console.Write(nextKey.KeyChar);
Console.Write(" ");
Console.Write(nextKey.KeyChar);
}
}
else
{
password.AppendChar(nextKey.KeyChar);
Console.Write("*");
}

nextKey = Console.ReadKey(true);
}

Console.WriteLine();

// lock the password down


password.MakeReadOnly();
return password
}

Getting data out of the SecureString can be done with the help of the marshaller. The Marshal
class has been extended to provide methods that convert a SecureString into a BSTR or a raw
block of ANSI or Unicode memory. Once you're done using the unprotected string, you need to
make sure to erase that copy. This can be done with some additional methods added to the
Marshal class. For instance, a correct usage pattern would be:

IntPtr bstr = Marshal.SecureStringToBSTR(password);

try
{
// ...
// use the bstr
// ...
}
finally
{
Marshal.ZeroFreeBSTR(bstr);
}

You might also like