Software Development optimize

Protect sensitive data with .NET 2.0's SecureString class


One of the trickier aspects of development is working with sensitive data such as social security numbers, employee identification numbers, and so forth. The .NET Framework includes cryptography functionality, but it is often overkill for smaller applications. Beginning with .NET Framework 2.0, the SecureString class is available to easily hold encrypted data in memory. Here are details about using the SecureString class.

When you should use the SecureString class

You may be wondering why the SecureString class is necessary when the basic String class is readily available. The problem lies in the design of the String class. For instance, if you have an instance of the String class that contains a person's social security number, you will want to securely store it during application processing to keep it from prying eyes.

The key issue is securely storing text as the String class stores it as plain text. Discovering where the data will be stored requires some understanding of .NET strings. .NET strings are immutable; that is, every change to the string value creates a new string in memory. Consequently, there are various copies of sensitive data in memory that may be accessed by less than scrupulous code or users.

This is not the most efficient approach and explains the existence of the StringBuilder object. You may think this is not much of a problem due to garbage collection, but memory reclamation with string is not deterministic with the .NET garbage collection. So, you should avoid the String class if the data is truly sensitive and examine other alternatives like the SecureString class.

Where to find it

You can find the SecureString class in the System.Security namespace. The Microsoft documentation describes the SecureString class as representing text that needs to remain confidential because it is encrypted in memory. The SecureString class has the following features:

  • String values are encrypted when stored in memory. It utilizes the Data Protection API (DPAPI), so it only works on NT-based platforms.
  • The SecureString class follows the IDisposable pattern.
  • The encrypted text cannot be retrieved easily; that is, there is no ToString class to quickly read the contents (no one ever said encryption was easy).

The constructor of the SecureString class is overloaded to allow you to create an instance of the class two ways, as the following list outlines:

  • SecureString(): A new instance of the class is initialized with no value.
  • SecureString(Char*, Int32): A new instance of the class is initialized given a subarray of Char objects and its length.

With the object initialized, you may use the various methods and properties of the class to work with its contents. The following list provides an overview of the more important properties and methods available:

  • AppendChar: Allows you to add one character to the end of the string stored by the class.
  • InsertAt: Allows you to insert a character in the string at a specific position.
  • RemoveAt: Allows you to remove a character from the string at a specific position.
  • SetAt(int, char): Allows you to populate a character value at the specific position.
  • Clear: Deletes the contents of the string value.
  • Copy: Creates a copy of the string value stored in the object.
  • Dispose: Releases all resources used by the object.
  • Length: Returns the length of the secure string value.
  • MakeReadOnly: Makes the contents of the string read-only. This is irreversible.
  • IsReadOny: Signals whether the contents of the object are read-only.

The following C# code creates an instance of the SecureString class and stores a data value in it.

using System;

using System.Collections.Generic;

using System.Text;

namespace SecureString {

class Program {

static void Main(string[] args) {

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

ss.AppendChar('T');

ss.AppendChar('e');

ss.AppendChar('c');

ss.AppendChar('h');

ss.AppendChar('R');

ss.AppendChar('e');

ss.AppendChar('p');

ss.AppendChar('u');

ss.AppendChar('b');

ss.AppendChar('l');

ss.AppendChar('i');

ss.AppendChar('c');

ss.AppendChar('.');

ss.AppendChar('c');

ss.AppendChar('o');

ss.AppendChar('m');

ss.MakeReadOnly();

Console.WriteLine(ss);

} } }

The code locks the string value with the MakeReadOnly method once the final character has been added. This means the value may not be altered. An exception is thrown if you attempt to alter the data once making it read-only. You'll notice the code simply displays the name of the class (System.Security.SecureString) when it executes. The equivalent VB.NET code follows:

Module Module1

Sub Main()

Dim ss As New System.Security.SecureString()

ss.AppendChar("T")

ss.AppendChar("e")

ss.AppendChar("c")

ss.AppendChar("h")

ss.AppendChar("R")

ss.AppendChar("e")

ss.AppendChar("p")

ss.AppendChar("u")

ss.AppendChar("b")

ss.AppendChar("l")

ss.AppendChar("i")

ss.AppendChar("c")

ss.AppendChar(".")

ss.AppendChar("c")

ss.AppendChar("o")

ss.AppendChar("m")

ss.MakeReadOnly()

Console.WriteLine(ss)

End Sub

End Module

Getting the data

Like most objects in the .NET Framework, the SecureString class provides a ToString method. But, the ToString method is derived from the base System.Object class, and it is not overridden in SecureString. Consequently, calling the ToString method of the SecureString class will only display the type of object (System.Security.SecureString) and no actual data.

The tricky aspect of working with the SecureString class is retrieving the data stored in it. Since it uses the encryption services of Windows, you need to utilize it, which requires using the System.Runtime namespace. The following C# line is the first step in retrieving the value. It copies the contents of the SecureString class into a long pointer object.

IntPtr pointerName = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(SecureString object);

Once the pointer object is populated, the pointer is converted to a string via the System.Runtime.InteropServices.Marshal class, as the following C# accomplishes.

Console.WriteLine(System.Runtime.InteropServices.Marshal.PtrToStringBSTR(bstr));

The Microsoft documentation states that converting the object to a pointer allocates the unmanaged memory required for a string, so you should always free the pointer object when finished by calling the ZeroFreeBSTR method, as the following C# accomplishes:

System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(bstr);

The following C# code marries the example of storing data in a SecureString object with retrieving it to store and display the contents.

using System;

using System.Collections.Generic;

using System.Text;

namespace SecureString {

class Program {

static void Main(string[] args) {

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

ss.AppendChar('T');

ss.AppendChar('e');

ss.AppendChar('c');

ss.AppendChar('h');

ss.AppendChar('R');

ss.AppendChar('e');

ss.AppendChar('p');

ss.AppendChar('u');

ss.AppendChar('b');

ss.AppendChar('l');

ss.AppendChar('i');

ss.AppendChar('c');

ss.AppendChar('.');

ss.AppendChar('c');

ss.AppendChar('o');

ss.AppendChar('m');

Console.WriteLine(ss);

IntPtr ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(ss);

try {

Console.WriteLine(System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr));

} finally {

System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr);

} } } }

The equivalent VB.NET code follows:

Module Module1

Sub Main()

Dim ss As New System.Security.SecureString()

ss.AppendChar("T")

ss.AppendChar("e")

ss.AppendChar("c")

ss.AppendChar("h")

ss.AppendChar("R")

ss.AppendChar("e")

ss.AppendChar("p")

ss.AppendChar("u")

ss.AppendChar("b")

ss.AppendChar("l")

ss.AppendChar("i")

ss.AppendChar("c")

ss.AppendChar(".")

ss.AppendChar("c")

ss.AppendChar("o")

ss.AppendChar("m")

Console.WriteLine(ss)

Dim ptr As IntPtr

ptr = System.Runtime.InteropServices.Marshal.SecureStringToBSTR(ss)

Try

Console.WriteLine(System.Runtime.InteropServices.Marshal.PtrToStringBSTR(ptr))

Finally

System.Runtime.InteropServices.Marshal.ZeroFreeBSTR(ptr)

End Try

End Sub

End Module

Protect your data with SecureString

The SecureString class provides the functionality to work with sensitive pieces of data within a .NET application without requiring you to use cryptography services or extensive code to encrypt data items. The SecureString class also lets you easily store string values and retrieve them on the Windows platform since the basic String class is less than appealing for working with sensitive data.

How do you handle sensitive data in your applications? Do you utilize encryption or another approach? Share your experience with the .NET community.

Tony Patton began his professional career as an application developer earning Java, VB, Lotus, and XML certifications to bolster his knowledge.

---------------------------------------------------------------------------------------

Get weekly .NET tips in your inbox TechRepublic's free .NET newsletter, delivered each Wednesday, contains useful tips and coding examples on topics such as Web services, ASP.NET, ADO.NET, and Visual Studio .NET. Automatically subscribe today!

About

Tony Patton has worn many hats over his 15+ years in the IT industry while witnessing many technologies come and go. He currently focuses on .NET and Web Development while trying to grasp the many facets of supporting such technologies in a productio...

5 comments
heritshah
heritshah

Lets assume i have one connection string defined in Module which has sensitive db pass n other information which i seldom use it in all my forms. here you use console.writeline method which is accomplished once, and pointer is terminated, cleared thereafter. but in my case there is no certainty about how long do i need to use my "connectionstring". How can i secure string with this method, keeping in mind i need to use it again n again.

hpham8
hpham8

Calling to Marshal.PtrToStringBSTR(ptr) creates a System.String that has the same content with ss. You end up having another copy of the password that has all the problem that you try to avoid in the first place. It defeats the purpose of having SecureString.

aikimark
aikimark

You have an example type in the "Getting the data" section. "IntPtr pointerName =" and ".PtrToStringBSTR(bstr));" are inconsistent. One of them should be changed to be consistent with later examples.

SnoopDoug
SnoopDoug

You say "You?ll notice the code simply displays the name of the class (System.Security.SecureString) when it executes." This is incorrect. The output is "TechRepublic.com". The editor owes me a brewski. doug in Seattle

MadestroITSolutions
MadestroITSolutions

At least for me it is. I am running the code in version 2.0.50727 of the framework and it does output the name of the class, not the contents. One thing worth noticing though is that the VB.NET class has only one constructor with no arguments as VB.NET does not provide pointer functionality and the AppendChar lines will blow if you have OPTION STRICT ON unless you explicitly cast the values to Char type. In C# that is not the case since single quote denotes Char and Double quote denotes String.