There is a String Class, and then there is a SecureString Class

Greetings to my blog readers!

Have you ever worked on an application that required accepting a password or other sensitive data from a user? If yes, then how did you handle the password retrieval and storage? Since most passwords, or other sensitive data like national insurance number, can contain alphanumeric values, it is sensible to treat them as strings. In C#, string or String is an immutable reference type with value-like semantics. This means that passing a string as a parameter to a method that locally modifies its value results in locally changed copy, with the original string unchanged. One approach to handling passwords can be to accept user’s input as a string, encrypt or hash it, and then store away the encrypted value. The unencrypted string would be collected by the Garbage Collector (GC). The problem with this approach is that until the GC gets around to reclaim the managed memory, the password exists in its unencrypted form in the process’s heap. This may present a security gap.

C# offers a very useful class for sensitive data processing. The class is SecureString. It allows one to allocate a block of unmanaged memory and encrypt its content at initialization. The class has no built-in methods for decrypting the string back, which makes it more secure. Let’s look at an example:

using System;
using System.Security;

namespace MySecureString
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.WriteLine("Please enter your password:");

            // SecureString implements IDisposable
            using (SecureString password = new SecureString())
            { 
                ConsoleKeyInfo nextKey = Console.ReadKey(true);
                while (nextKey.Key != ConsoleKey.Enter)
                {
                    password.AppendChar(nextKey.KeyChar);
                    Console.Write("*");
                    nextKey = Console.ReadKey(true);
                }

                // makes the password immutable
                password.MakeReadOnly();
                // ...
            }
        }
    }
}

In the example code above we are creating an instance of SecureString and initializing it with the user’s input. Note that I wrapped the example in the using block to avoid unmanaged memory leak. A call to MakeReadOnly ensures that the encrypted password cannot be modified, which SecureString class achieves by maintaining an internal boolean flag.

Ok, so how does this actually work? The data encryption is a process of several stages. SecureString class makes a call to the native Win32 SystemFunction040() method passing it the pointer to the memory buffer with user’s input, its length and the CRYPTPROTECTMEMORY_SAME_PROCESS flag. This method encrypts the memory in place, meaning that the memory buffer is overridden with encrypted content. The flag sets the rules on which process is allowed to decrypt this memory content (e.g. the current process only). The encryption is done according to the Windows Data Protection API (DPAPI), which offers password-based data protection service. In short, the data protections is done using a MasterKey, derived with the help of user’s system logon password, and a session key. An additional ‘secret’ word can be supplied to strengthen security. Only the MasterKey is stored in the user’s profile, to allow for later decryption of the data. By default, MasterKey is stored in the user’s profile folder, which is %AppData%/Microsoft/Protect. Also, by default, the key expires every three months, after which it gets re-generated.

Since SecureString class does not expose any methods for decryption, a call to the Windows native SecureStringToBSTR method has to be made. Here is an example of how to decrypt the password:

IntPtr bstr = IntPtr.Zero;
// it is important to free the memory pointed to by IntPtr
// therefor we use try/finally block
try
{
    bstr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(password);
    Console.WriteLine("Original password is: " + System.Runtime.InteropServices.Marshal.PtrToStringUni(bstr));
}
finally
{
    System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(bstr);
}

Hope this post has at least triggered your curiosity about DPAPI. Thank you for reading!