Secure Strings in .NET - Part I

Hi Gaurav Sharma here.......

I am a developer on the CISG India team based in Hyderabad and I joined Microsoft four months ago. I love playing computer games and recently finished Call of duty 4. For the last three years I've been working with .NET and have worked on different kinds of applications that include ERP solutions, utilities and web portals. Apart from computer games I like watching soccer; there was a time (long back) when I used to watch each and every match of English premier league. I've a blog that I last updated when I joined MS. This looks like enough information about me, let us start with our secure string story.

A few months back I was looking into improvements that Microsoft made to it's .NET base class library since its inception and I stumbled upon a class named Secure String. I found that exiting and made a small blog entry related to this new class.

In this write up I'll try to focus on the need for secure string class as well as its internal working and usage. I'll also try to do a feature comparison between String and SecureString class. You will find code examples and IL code with theoretical explanations. There are a few tools that I will use for creating code and IL samples. These are:

  • Visual Studio 2008
    I'll use Visual studio for creating code samples. If you do not have Visual studio installed on your system you can download and install the free express version of our beloved IDE.
  • .NET class viewer
    I will use .NET Reflector but you can use any tool of your choice.
  • ILDASM.EXE
    Another great tool and will be used to look at IL code, manifest information and assembly metadata
  • Memory Profiler
    I'll try to use this tool to show how object creation of string class differs from secure string

The structure of this two part write up (this is part one) is as follows;

  1. Part I
    • Introduction
    • SecureString Class
    • Members
    • Usage
  2. Part 2
    • Internals
    • SecureString vs. String class
      • Performance
      • Usability
    • Misc.

Let's start our journey to explore SecureString,

INTRODUCTION

Since the inception of .NET programming, I doubt whether there is any other class used more frequently than the String class. We can use string to represent any textual and literal data like name, location, occupation etc. There are four ways in which we can represent individual character and their collections:

  1. System.Char
  2. System.String
  3. System.Text.StringBuilder, and
  4. System.Security.SecureString

System.String is an ordered set of characters that is immutable. String is a reference type so every string object that we create lives on the heap rather than on the thread stack. Because strings are immutable, once they are created we can not change them. We can not add or remove or manipulate existing string objects. Whenever we try to manipulate string objects we discard old objects (they are still there on heap) and create new ones. Let's see a code snippet,

    1:  using System;
    2:   
    3:  namespace Gaurav.Samples.SecureString
    4:  {
    5:      class Program
    6:      {
    7:          static void Main(string[] args)
    8:          {
    9:              string name = "Gaurav";
   10:              name = name + "Sharma";
   11:              name = name + "Mr.";
   12:              Console.WriteLine(name);
   13:              Console.Read();
   14:          }
   15:      }
   16:  }

This will create three string objects. When we add "Sharma" to the already existing string name, a new string object is created and the name variable now refers to this new object. The old string object is marked for garbage collection. Same thing happens again when we try to append "Mr." to name. A new object is created and we have two old objects marked for garbage collection. The next garbage collection run will clear out these two objects.

Now suppose you have a application that stores username, password and credit card number in string objects. These string objects are basically in memory character arrays and if there is any unsafe or unmanaged code is allowed to execute, that code can snoop around the process's address space, locate the string with sensitive data and can use this data in a bad way.

Strings are not pinned objects. Pinned objects are those whose memory can not be compacted by garbage collection. As strings are not pinned objects, our garbage collector is  free to move strings in memory. When string objects are moved several garbage copies of string data are created whose memory will be reclaimed by the garbage collector during later runs. Having so many copies of data (especially if sensitive) can create issues.

String keeps a plain text copy i.e. not encrypted, so our data is under continuous danger of being read by some one who can read our process's memory. If our string object is used for a small time span and then garbage collected, it's possible that the common language runtime (CLR) will not reuse the object's memory immediately (this depends on the generation of the object). This can be a potentially compromising situation with our strings data left in memory. Moreover as strings are immutable you can not overwrite them with blank data, the code below for example will not clear out any data.

    1:  using System;
    2:   
    3:  namespace Gaurav.Samples.SecureString
    4:  {
    5:      class Program
    6:      {
    7:          static void Main(string[] args)
    8:          {
    9:              string name = "Gaurav";
   10:              //this will create new object and will not clear out old object's data
   11:              name = "";
   12:              Console.WriteLine(name);
   13:              Console.Read();
   14:          }
   15:      }
   16:  }

If at any time you run out of memory, string memory contents can get into SWAP files, where they can be a lot easier to access by the bad guys (hackers). Due to these issues Microsoft introduced added another secure string class in FCL,

  1. Class Name: SecureString
  2. Namespace: System.Security
  3. Assembly: mscorlib.dll

SECURE STRING CLASS

Some facts about secure string class are:

  1. When a new object of secure string class is constructed, the CLR allocates a block of "unmanaged" memory that contains an array of characters. The garbage collector is not aware of this memory block because this is unmanaged.
  2. Data is encrypted using Data Protection API or DPAPI .
  3. Methods available in Secure String class like AppendChar, InsertAt...first decrypts the data then performs the action and then again re-encrypts the data.
  4. This class implements the IDisposable interface which means that you can clean out your object deterministically. Dispose method zeroes out the content of the memory buffer.
  5. This class is also derived from CriticalFinalizerObject . This guarantees that object's finalizer method is called , which means characters will be zeroed out and memory will be freed. 
  6. Unlike string, objects are mutable. This means that if we pass this object to different functions, only one copy of object will be there.
  7. This is non CLS compliant (use of pointers).
  8. This is a sealed class (like all data types).

MEMBERS

MSDN provides a good information about members of  the SecureString class. https://msdn.microsoft.com/en-us/library/system.security.securestring_members.aspx page will give you enough information that will equip you for SecureString usage. For your convenience I'll add member summary here, but for latest information you should always check MSDN.

memberInfo

 USAGE

There are not many examples that uses SecureString class so I'll try to show you where this class is used in the framework class library by developing a small application that uses secure string. To avoid complications our sample application will be a small (micro) console application that stores passwords in secure string and renders it to console. Let's start with knowing about FCL usage of SecureString class,

  1. Framework Class Library usage

    • System.Diagnostics

      • System.Diagnostics.Eventing.Reader.EventLogSession..ctor(String, String, String, SecureString, SessionAuthentication) [.ctor=constructor in IL]
      • System.Diagnostics.Process.Start(String, String, SecureString, String) : Process
      • System.Diagnostics.Process.Start(String, String, String, SecureString, String) : Process
      • System.Diagnostics.ProcessStartInfo.Password : SecureString
    • System.Runtime.InteropServices

      • System.Runtime.InteropServices.Marshal.SecureStringToBSTR(SecureString) : IntPtr
      • System.Runtime.InteropServices.Marshal.SecureStringToCoTaskMemAnsi(SecureString) : IntPtr
      • System.Runtime.InteropServices.Marshal.SecureStringToCoTaskMemUnicode(SecureString) : IntPtr
      • System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocAnsi(SecureString) : IntPtr
      • System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocUnicode(SecureString) : IntPtr
    • System.Security.Cryptography

      • System.Security.Cryptography.CspParameters..ctor(Int32, String, String, CryptoKeySecurity, SecureString) [CSP = Crypto Service Providers]
      • System.Security.Cryptography.CspParameters.KeyPassword : SecureString
      • System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[], SecureString)
      • System.Security.Cryptography.X509Certificates.X509Certificate..ctor(Byte[], SecureString, X509KeyStorageFlags)
      • System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String, SecureString)
      • System.Security.Cryptography.X509Certificates.X509Certificate..ctor(String, SecureString, X509KeyStorageFlags)
      • System.Security.Cryptography.X509Certificates.X509Certificate.Export(X509ContentType, SecureString) : Byte[]
      • System.Security.Cryptography.X509Certificates.X509Certificate.Import(Byte[], SecureString, X509KeyStorageFlags) : Void
      • System.Security.Cryptography.X509Certificates.X509Certificate.Import(String, SecureString, X509KeyStorageFlags) : Void
      • System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[], SecureString)
      • System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(Byte[], SecureString, X509KeyStorageFlags)
      • System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String, SecureString)
      • System.Security.Cryptography.X509Certificates.X509Certificate2..ctor(String, SecureString, X509KeyStorageFlags)
      • System.Security.Cryptography.X509Certificates.X509Certificate2.Import(Byte[], SecureString, X509KeyStorageFlags) : Void
      • System.Security.Cryptography.X509Certificates.X509Certificate2.Import(String, SecureString, X509KeyStorageFlags) : Void
    • System.Windows.Controls

      • System.Windows.Controls.PasswordBox.SecurePassword : SecureString
      • System.Windows.Controls.PasswordBox.SetSecurePassword(SecureString) : Void
      • System.Windows.Controls.PasswordTextContainer..ctor(PasswordBox)
      • System.Windows.Controls.PasswordTextContainer.SetPassword(SecureString) : Void

Below I've added what's probably the most exhaustive list of SecureString usage you will ever find on web. Some of these uses secure string internally and some of them are not public. You will find a list of places where SecureString is used, places where SecureString objects are exposed, where SecureString objects are initialised and assemblies on which SecureString depends.

InitBy 
usedBy

dependsUpon

Now let's move to our sample application.

  1. Sample application
    • Create a C# console application. You can name it as you like. Our application is very simple. It takes a password string from user and renders it back to the console.

    • Our sample application will have two methods, one Main [entry point] and other to render secure string on standard output stream [console in our case]

    • Make sure you check unsafe usage from project build properties window. This will add '/unsafe' switch during compilation. '/unsafe' compilation switch is required to compile any unsafe code. Important thing is that we don't need '/unsafe' switch to create secure string objects, this check is required to access secure string's unmanaged memory location using pointers. If you use CSC.EXE [C # compiler] from console you can use '/unsafe' switch to compile unsafe code.

      prjProperties 

    • Our class looks like this

          1:  using System;
      
          2:  using System.Security; //required for SecureString
      
          3:  using System.Runtime.InteropServices; //required for SecureString
      
          4:   
      
          5:  namespace Gaurav.SecureStringSamples.ConsoleLoginSample
      
          6:  {
      
          7:      class Program
      
          8:      {
      
          9:          static void Main(string[] args)
      
         10:          {
      
         11:              using (SecureString password = new SecureString())
      
         12:              {
      
         13:                  ConsoleKeyInfo consoleKeyInfo; //used to read console keys
      
         14:                  Console.Write("Please enter password: ");
      
         15:                  while (true)
      
         16:                  {
      
         17:                      consoleKeyInfo = Console.ReadKey(true);
      
         18:                      if (consoleKeyInfo.Key == ConsoleKey.Enter) break;
      
         19:                      password.AppendChar(consoleKeyInfo.KeyChar);
      
         20:                      Console.WriteLine("*");
      
         21:                  }
      
         22:                  Console.WriteLine();
      
         23:                  RenderSecureString(password);
      
         24:                  Console.Read();
      
         25:              }
      
         26:              Console.WriteLine("done");
      
         27:              Console.Read();
      
         28:          }
      
         29:          private unsafe static void RenderSecureString(SecureString toBeDisplayedSecureStr)
      
         30:          {
      
         31:              Char* charPtr = null;
      
         32:              try
      
         33:              {
      
         34:                  //cast int ptr to char ptr
      
         35:                  //copy content from secure string to block of unmanaged memory
      
         36:                  charPtr = (Char*)Marshal.SecureStringToCoTaskMemUnicode(toBeDisplayedSecureStr);
      
         37:                  for (Int32 count = 0; count < toBeDisplayedSecureStr.Length; count++) Console.Write(charPtr[count]);
      
         38:              }
      
         39:              finally
      
         40:              {
      
         41:                  //frees unmanaged string pointer allocated by Marshal.SecureStringToCoTaskMemUnicode
      
         42:                  if (charPtr != null) Marshal.ZeroFreeCoTaskMemUnicode((IntPtr)charPtr);
      
         43:              }
      
         44:          }
      
         45:      }
      
         46:  }
       
      
    • Output window will look something like this

      outputOne

Cool, we have now created our very first secure string example. Next time I'll add some performance tests to this example and compare them with the standard string class.

Stay tuned!