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 versiAnonymous
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.
-ShawnAnonymous
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.
-ShawnAnonymous
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.&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 SubAnonymous
November 01, 2006
A few times over the last couple of days discussion about a tool on the Internet which can attach toAnonymous
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. -ShawnAnonymous
February 05, 2007
The comment has been removedAnonymous
March 19, 2007
SecureString Class "Represents text that should be kept confidential. The text is encrypted for privacyAnonymous
April 12, 2009
PingBack from http://www.hardcodet.net/2009/04/dpapi-string-encryption-and-extension-methodsAnonymous
April 22, 2009
Every time you need to store sensitive data your first thought use to be encryption. You probably gatherAnonymous
October 12, 2009
The comment has been removedAnonymous
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.