SYSK 74: Are You Using SecureString Class?

The new System.Security.SecureString type (introduced in .NET 2.0) uses DPAPI to help ensure that sensitive data stored in string form is not exposed to memory or disk-sniffing attacks.  The data is held in memory in the encrypted format, and is only decrypted when accessed.

 

So, you can figure it’s great for storing secrets – passwords, cached connection strings, and other sensitive data. 

 

But first, why is the string class not sufficient?  For at least 4 reasons:

  • There is no way to erase them
  • GC doesn’t zero out old memory.
  • GC compaction is NOT deterministic
  • GC might move strings around and create several instances of the secret.

 

The new SecureString class stores your content encrypted, you can deleted when no longer needed, the class and the data is not visible to COM (even though DPAPI is used for encryption).  You can lockdown the string to prevent any changes to it.  And, finally, there are no methods to inspect, compare or convert content.

 

Here is an example on how you’d use it:

 

System.Security.SecureString secureData = new System.Security.SecureString();

// You really should read it from I/O one character at a time

// This defeats the whole purpose, but this is just an example

string rawData = "blah blah blah...";

foreach(char c in rawData.ToCharArray())

{

    secureData.AppendChar(c);

}

// No more changes after this!

secureData.MakeReadOnly();

IntPtr uniString = System.Runtime.InteropServices.Marshal.SecureStringToGlobalAllocUnicode(secureData);

// Get raw data back... Again – not secure; only shown as an example on how to get the data back. In “real life”, do not copy the content to un-secure string.

String rawData2 = System.Runtime.InteropServices.Marshal.PtrToStringUni(uniString);

if (rawData2 == rawData)

    MessageBox.Show(string.Format("Got it back Ok... {0}", rawData2));

else

    MessageBox.Show(string.Format("Got back something different -- {0}", rawData2));

// Destroy from memory

System.Runtime.InteropServices.Marshal.ZeroFreeGlobalAllocUnicode(uniString);

 

Reference: TechEd 2005, Rajiv Sodhi’s presentation