Friday, March 31, 2006 10:53 AM
Talking about System.Security.SecureString
Within a couple of weeks, our series of MSDN Security Evenings will go on air at various locations (see http://community.bartdesmet.net/blogs/bart/archive/2006/03/25/3841.aspx for more information). It's pretty clear developers care about security (maybe just not enough yet) and so I received a couple of mails last week with questions about security in the world of .NET. Keep those mails coming guys! One of the questions was about the new System.Security.SecureString class. The MSDN Documentation is rather limited:
Represents text that should be kept confidential. The text is encrypted for privacy when being used, and deleted from computer memory when no longer needed. This class cannot be inherited.
And so, I decided to write a bit more about this new feature. Actually, the most common problem with the .NET Framework documentation is the lack of "forward references" which indicate where a certain class is being used elsewhere in the framework. Of course, this wouldn't make sense for things such as System.Int32 or System.String but for something like System.Security.SecureString it would be a handy piece of documentation indeed. Samples on how to use this class in a real scenario are just missing, so read on to learn more.
What's in a name?
So, what's the goal of the System.Security.SecureString class, or even more, why is the System.String class not secure that we need a new class? The answer is not so straightforward and you need a bit of insight on how strings work in the .NET Framework and what the troubles are because of the "string approach". Assume you have a string which contains a credit card number, or a social security ID, or a password, or a pin-code, or whatever that has to remain confidential. In order for your application to work with it, you'll need to keep that secret somewhere in memory. But where's "somewhere"? And how is the thing stored? Both questions deserve a proper answer. The latter one is the most straightforward to answer and its answer is just "in plain text". So, when somebody can read your memory (which is - as the matter in fact - not so difficult to do if security on the machine is compromised; notice a buffer overrun attack isn't a possibility in safe verifiable managed code), the data can be stolen pretty easy. The other question about where the data lives is a bit more difficult to understand. First of all, strings in .NET are immutable. This means that string concatenation creates a new string (if you aren't using the System.Text.StringBuilder utility class, see http://community.bartdesmet.net/blogs/bart/archive/2005/10/04/3583.aspx). Assume the following:
string password = "";
char someChar = ...; //get from Console.ReadKey() for example
password += someChar;
Over here, you're just creating a bunch of additional intermediary string instances which contain fragments of the password the user is typing: "H", "He", "Hel", "Hell", "Hello", ... This wouldn't be much of a problem if you could remove the string from memory in a deterministic way when it's not used anymore. However, memory reclaim of strings is totally undeterministic due to the use of the garbage collector. As the matter in fact, the garbage collector also introduces an additional problem. As you know, the managed memory space is divided in generations. When an object survives generation 0, it will be propagated to generation 1 (the same applies for generation 1 to generation 2). Basically, this means that a copy of the original string is created and stored elsewhere in memory. The phantoms will eventually be overwritten but you don't know when. So, you know when the confidential information enters the memory of your computer, but you don't know when it leaves again. You're out of control now :S.
This is where the SecureString comes into play. Basically, it solves the two problems listed above as follows:
- the string content is stored in memory in an encrypted fashion, using DPAPI (because of this, it will only work on NT-based platforms and not on Windows 98 or Millennium (in contrast to what the "Platforms" section in the MSDN documentation tells you);
- to allow more deterministic clean-up, SecureString follows the IDisposable pattern;
- you can't retrieve the stored string in clean text easily from a SecureString instance, to reduce the risk of fatal accidents (once a SecureString becomes a normal String, all problems are reintroduced again).
So, how does this thing work? Let's start with the signature of SecureString:
public sealed class SecureString : CriticalFinalizerObject, IDisposable
The key takeaway from this signature is that the SecureString class implements IDisposable and so you will do something like:
using (SecureString str = new SecureString())
to ensure proper cleanup by calling the Dispose method implicitly (and automatically). Secondly, the SecureString class inherits from the System.Runtime.ConstrainedExecution.CriticalFinalizerObject abstract class. The CriticalFinalizerObject ensures that finalization code is marked as critical and is allowed to run always. In order to do make this possible, the finalization code must follow the rules of a Constrained Execution Region. Integration of the CLR in SQL Servr 2005 was one of the reasons why CER's where born in .NET v2.0 in order to allow the CLR to be hosted in a reliable environment where proper clean-up is a must-have (think of a transactional context). Due to this implementation, the finalizer is allowed to run always, even when the runtime performs a rude thread abort or unloads an application domain, cases where finalization of normal objects is not always guaranteed.
Inside the SecureString class, data is just kept in a piece of unmanaged memory space, outside the control of the garbage collector. By doing so, the user/application is in control of the time when the memory space is cleaned up, rather than having to rely on the garbage collector to come around. On to the relevant public members of the class:
- void AppendChar(char) - appends one character to the end of the string
- void InsertAt(int, char) - inserts a character in the string on the given position
- void RemoveAt(int) - removes a character from the string on the given position
- void SetAt(int, char) - sets a character in the string on the given position
- void Clear() - deletes the string's value
- SecureString Copy() - creates a copy of the string
- void IDisposable:Dispose() - releases all resources used by the SecureString instance behind the scenes
- void MakeReadOnly() - makes the string read-only (irreversible)
- bool IsReadOny() - checks whether the string is read-only
As you can see, it's impossible to get the contents of a SecureString instance directly. The ToString() method derived from System.Object isn't overridden either, to prevent you from getting the secure data accidentally in managed memory space, with all the associated problems mentioned above.
Getting the data
Putting data in there is one thing, but ghetting it out of it is another crucial thing. If you can't (and shouldn't even try) to convert a SecureString to a normal String instance, how to use the data inside? The answer is using the Marshal class to convert the SecureString to ANSI or Unicode or a BSTR. Those methods (of System.Runtime.InteropServices.Marshal) include:
Once you've used the data, you need to free (and zero out) the unmanaged memory space you've been using to put the (decrypted) string in. The code pattern looks like this:
IntPtr bstr = Marshal.SecureStringToBSTR(ss);
// use the bstr
if (IntPtr.Zero != bstr)
An alternative is to use unsafe code with a pointer in C#:
// use the string
if (null != pDecryptedString)
Marshal.ZeroFreeCoTaskMemUnicode(pc as IntPtr);
The code listed above is only applicable to developers who need to get a password (or another secret) in a secure fashion and forward it to an (unmanaged) piece of code. However, as a "normal" developer you're likely more interested in BCL classes which use SecureString out of the box.
Let's take a look at the BCL classes that use SecureString first of all. When investigating the .NET Framework API you'll come to the conclusion SecureString isn't used that much yet. One place where you'll find SecureString is in the System.Diagnostics.ProcessStartInfo's Password property. Other places I'm aware of are the System.Security.Cryptography classes (more specifically when working with Crypto Service Providers and X.509 Certificates). Useful 3rd party projects include the following:
The most common SecureString example however is to use the Console to read a password and store it in a SecureString instance. A sample can be found on the .NET Security Blog but notice keys such as F1, Home, End, media buttons, etc are intercepted by Console.ReadKey(true) too, so you might want to add additional logic to filter those out (see ConsoleKey class). Also don't forget to apply a MakeReadOnly() at the end.Del.icio.us
| Digg It
Filed under: .NET Framework v2.0