Making Strings More Secure

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);
}

Updated 11/26/04: Fixed a bug in GetPassword

Comments

  • Anonymous
    May 27, 2004
    Shawn posted an interesting piece on making strings more secure under Whidbey. Using the System.String class has never been considered secure because it was always quite easy for the data to move in memory, potentially leaving a footprint of sensitive data such as passwords in various parts of memory. In Whidbey, Microsoft has introduced a new object called SecureString that does just that. Using the Data Protection API (DPAPI) the string will be held in memory encrypted until which time it is used, making it much more difficult to read. Further to this the data is pinned, preventing multiple copies to be scattered around in different memory locations. Shawn does a great job of explaining how this all works, so I will leave the excercise of learning just how to use this to you... by reading his post on SecureString....

  • Anonymous
    June 04, 2004
    La versi

  • Anonymous
    June 07, 2004
    Sorry, I'm a bit lost. What good does this do? To use the byte[] or string, won't I still have to move it into a normal String to use existing APIs? If an attacker already has access to the machine's memory, what's the point? What real-world scenario would this really help secure?

  • Anonymous
    June 07, 2004
    Hi Ben,

    Good post, you bring up some interesting and important points. Let me address your second point first. Since SecureString stores its value in encrypted memory it greatly reduces the surface area of attack. Rather than having potentially several copies of an unencrypted password or credit card number (due to String being immutable and movable by the GC), you now have one copy that's encrypted. And yes, even though you will have to decrypt the data to use it, you're reducing the amount of time that the sensitive information is visible to memory snoopers, which serves to raise the bar for attack.

    Granted, that if someone has enough access to spy on your process' address space, if they're determined enough, they'll get at the contents of the SecureString, either by waiting around for it to be decrypted or through some other method. Anyone who can debug your application owns your application.

    However, some other real world scenarios that this helps to mitigate is sensitive data being stored in the Windows page file. Since SecureStrings spend most of their lives encrypted, the odds that an unencrypted sensitive string will be swapped out are much smaller. Also, since there will only be one copy, the odds decrease even more.

    Another (potentially more interesting) issue that this helps to mitigate is with crash dumps. When Watson sends a crash dump report back to Microsoft, your SecureStrings will likely be encrypted in that report. Also, if you have your customers send you a crash dump of your applications so that you can debug, they can take some comfort that you won't be able to access their sensitive data. (Of course, this may make debugging more interesting :-) ).

    Your first point is dead on. SecureString will only continue to become more useful as more and more APIs begin to use it. Whidbey is only laying the groundwork for SecureString, but I'd imagine that other APIs will start to pop up that use this feature. I've already blogged about one: http://blogs.msdn.com/shawnfa/archive/2004/06/02/146915.aspx

    Third party library developers are now also free to use SecureString in their interfaces, which will also help increase the usefulness of the class. In an ideal world, eventually you'd be able to go end-to-end with your SecureString never being converted to a standard String.

    It's also important to remember that you won't necessarially have to convert to a String. 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.

    -Shawn

  • Anonymous
    June 21, 2004
    <sarcasm>Say it isn't so - a mutable String with a Clear method? Why didn't they just make the Length property read/write so that you could clear it by setting the Length to 0 like you have to do with StringBuilder? That's sooo obvious, right?</sarcasm>

    OT but seriously, why don't they add a Clear method to StringBuilder?

  • Anonymous
    June 21, 2004
    So this solves several more problems than a clearable StringBuilder would. Two of the more obvious ones are that the string is encrypted in memory and that it is pinned down so that it doesn't end up copied to 10 different places in memory by the garbage collector. Clearing of the secure string also does more than just set the length back to 0, it wipes them memory several times.

    SecureString will raise the bar higher than a StringBuilder with Clear(), but it also adds a perf cost. Chosing between them really depends on your situation.

    -Shawn

  • Anonymous
    November 06, 2004
    Microsoft should create a CommonDialog that wraps CredUIPromptForCredentialsW so that applications can easily benefit from a consistent password dialog.

  • Anonymous
    August 29, 2005
    Hi,

    In your sample code, you are reading the password from the console. In most applications, password are read from a System.Windows.Forms.TextBox control used with the PasswordChar property set, which provides the password as a... string. :-(
    Will the System.Windows.Forms.TextBox control feature a SecureString property, or will a new control be available in Whidbey?

    Jan.

  • Anonymous
    March 30, 2006
    Introduction
    Within a couple of weeks, our series of MSDN Security Evenings will go on air at various...

  • Anonymous
    April 10, 2006
    Hatteras has three tiers: client, middle, and data.&amp;nbsp; The middle tier is an ASP.NET web service on...

  • Anonymous
    May 10, 2006
    Here is how todo it in vb.net(2.0 framework)

    Function ConvertToSecureString(ByVal str As String)

           Dim password As New SecureString

           For Each c As Char In str.ToCharArray

               password.AppendChar(c)

           Next

           Return password

    End Function

    Sub Main()

          dim username as string = "Administrator"

          dim password as SecureString = ConvertToSecureString("my password")

          dim domain as string = Nothing

          dim filename as string = "notepad.exe" ' %SYSTEMROOT%system32

           Try

               System.Diagnostics.Process.Start(filename,username, password, domain)

           Catch ex As Win32Exception

               MessageBox.Show("Wrong username or password.", "Error logging in as administrator", MessageBoxButtons.OK, MessageBoxIcon.Error)

           End Try

    End Sub

  • Anonymous
    November 01, 2006
    A few times over the last couple of days discussion about a tool on the Internet which can attach to

  • Anonymous
    November 07, 2006
    I like the SecureString very much, but I was wondering why it is not supported in win98? (Seems to be an error in the documentation, which says that it does support win98). Would there not be some sort of mechanism in the future whereby methods can be id'ed so that only that method could have access to data in a class?

  • Anonymous
    November 14, 2006
    SecureString uses DPAPI under the hood, and that is not available on Win9x.  Please leave documentation feedback in MSDN if it is documented incorrectly. You can also feel free to use the MSDN Product Feedbcak Center if you want to request Intellisense know about which OS is supported for each API. -Shawn

  • Anonymous
    February 05, 2007
    The comment has been removed

  • Anonymous
    March 19, 2007
    SecureString Class "Represents text that should be kept confidential. The text is encrypted for privacy

  • Anonymous
    April 12, 2009
    PingBack from http://www.hardcodet.net/2009/04/dpapi-string-encryption-and-extension-methods

  • Anonymous
    April 22, 2009
    Every time you need to store sensitive data your first thought use to be encryption. You probably gather

  • Anonymous
    October 12, 2009
    The comment has been removed

  • Anonymous
    October 12, 2009
    > either by finding the ConsoleKey's sitting on the stack You've misunderstood the rules of possession and plurality again.

  • Anonymous
    November 05, 2009
    :-)  I was trying to seperate the s from the ConsoleKey so that it didn't look like I was talking about a ConsoleKeys class.  Perhaps a - would have been more appropriate.