Monday 26 October 2015

SecureString

I used the SecureString class to store passwords in memory a few times in the past, and I have realised that I used it pretty wrongly, so I'll try to put up here what I have learned lately so that I can avoid such misuse in the future.

Remove from memory
Sometimes you have data (secret data) that want to keep in memory for as short as possible, just for the instant that they are going to be used. While those data are in memory someone could get access to them, either by inspecting the memory with a debugger, the swap file where it might have ended up or even easily, by dumping the process memory to disk (in modern Windows this is as easy as it can get, just launch the Task Manager, right click on the process and select "create dump file"). The first thought in order to get rid of those sensitive data would be to release that memory, but in the .Net managed world this is not so simple. After setting the reference to null you need the GC to come into action, and even when you can force a GC.Collect, this is not immediate, you also have to be aware about generations and so on. So a better option seems to overwrite the involved memory. For this, the .Net string class is a pretty bad bet, as it is immutable, so you can not overwrite it. You should better use a mutable structure (like a char array)

As a side note, I wanted to remark here that though you should not use strings for your critical data, it is not as horrible bad as I had got to think. I was thinking that because of string interning a string could stay in memory forever, but that is not the case. The string interning table is stored int he LOH (large object heap) that though GC'ed very unfrequently, it is occasionally. Furthermore, by default runtime strings (for example the string with a password that you have just unencrypted in memory) won't be interned, by default interning only applies to string literals.

SecureString
OK, so you'll read everywhere that you must use SecureStrings for all your sensitive data, but how does it really work? A SecureString contains an encrypted version of the string that you want to store in it. The encryption/decryption of the value is based on a symmetric algorithm and a key specific to the current logon session (this is based on DAPI key management and is explained here). The weak point obviously is the clean, sensitive string that will be encrypted into the SecureString. Some framework classes work directly with SecureStrings, for example WPF's PasswordBox, meaning that if a user types a password in that box, each character will be appended to a SecureString, and you will not end up with the password in a normal string at all. The ProcessInfo.Password is also a SecureString, but unfortunately there are many situations where the support for SecureStrings is missing.

A common sceneario
A relatively common scenario is having an application that needs to start another process or another thread (impersonation) under a different identity (UserB). We'll have the UserB password encrypted with some symmetric key in a file, DB ... we'll read it and decrypt it into a SecureString. This is important, some Symmetric Encryption classes that I've used for this in the past were just returning me a String, which is pretty wrong. You can find here a good implementation that returns SecureStrings. Notice how they do the decryption to a char[], build a SecureString from it, and immediately clear the Array with the clean password.

  public void Decrypt(byte[] input, out SecureString output, byte[] key,
            byte[] iv)
        {
            byte[] decryptedBuffer = null;

            try
            {
                // do our normal decryption of a byte array
                decryptedBuffer = Decrypt(input, key, iv);

                char[] outputBuffer = null;
                
                try
                {
                    // convert the decrypted array to an explicit
                    // character array that we can "flush" later
                    outputBuffer = _utf8.GetChars(decryptedBuffer);

                    // Create the result and copy the characters
                    output = new SecureString();
                    try
                    {
                        for (int i = 0; i < outputBuffer.Length; i++)
                            output.AppendChar(outputBuffer[i]);
                        return;
                    }
                    finally
                    {
                        output.MakeReadOnly();
                    }
                }
                finally
                {
                    if (outputBuffer != null)
                        Array.Clear(outputBuffer, 0, outputBuffer.Length);
                }
            }
            finally
            {
                if (decryptedBuffer != null)
                    Array.Clear(decryptedBuffer, 0, decryptedBuffer.Length);
            }
        }

OK, so now we have our password in a SecureString. This is fine for starting a new Process cause as previously mentioned ProcessStartInfo.Password is a SecureString, but what about impersonation?

As you know for impersonation we need to obtain an AccessToken via the LogonUser Win32 function. The problem is that this function does expect a clean password, not a SecureString, so are we condemned to the risks of having a clean password in our managed memory until the GC decides to clean it?

Hopefully there is a solution that is nicely explained here. The main point is that you can declare the PInvoke signature for LogonUser as receiving an IntPtr for the password parameter rather than a string (I had always done the latter). Then, you have to use Marshal.SecureStringToGlobalAllocUnicode to decrypt and marshal the SecureString into unmanaged memory. Once you are finished with LogonUser, you have to clean up the unmanaged memory holding the unencrypted password by calling into Marshal.ZeroFreeGlobalAllocUnicode

No comments:

Post a Comment