When would I need a SecureString in .NET?

Some parts of the framework that currently use SecureString:

  • WPF's System.Windows.Controls.PasswordBox control keeps the password as a SecureString internally (exposed as a copy through PasswordBox::SecurePassword)
  • The System.Diagnostics.ProcessStartInfo::Password property is a SecureString
  • The constructor for X509Certificate2 takes a SecureString for the password

The main purpose is to reduce the attack surface, rather than eliminate it. SecureStrings are "pinned" in RAM so the Garbage Collector won't move it around or make copies of it. It also makes sure the plain text won't get written to the Swap file or in core dumps. The encryption is more like obfuscation and won't stop a determined hacker, though, who would be able to find the symmetric key used to encrypt and decrypt it.

As others have said, the reason you have to create a SecureString character-by-character is because of the first obvious flaw of doing otherwise: you presumably have the secret value as a plain string already, so what's the point?

SecureStrings are the first step in solving a Chicken-and-Egg problem, so even though most current scenarios require converting them back into regular strings to make any use of them at all, their existence in the framework now means better support for them in the future - at least to a point where your program doesn't have to be the weak link.


Short Answer

why can't I just say:

SecureString password = new SecureString("password");

Because now you have password in memory; with no way to wipe it - which is exactly the point of SecureString.

Long Answer

The reason SecureString exists is because you cannot use ZeroMemory to wipe sensitive data when you're done with it. It exists to solve an issue that exists because of the CLR.

In a regular native application you would call SecureZeroMemory:

Fills a block of memory with zeros.

Note: SecureZeroMemory is is identical to ZeroMemory, except the compiler won't optimize it away.

The problem is that you can't call ZeroMemory or SecureZeroMemory inside .NET. And in .NET strings are immutable; you can't even overwrite the contents of the string like you can do in other languages:

//Wipe out the password
for (int i=0; i<password.Length; i++)
   password[i] = \0;

So what can you do? How do we provide the ability in .NET to wipe a password, or credit card number from memory when we're done with it?

The only way it can be done would be to place the string in some native memory block, where you can then call ZeroMemory. A native memory object such as:

  • a BSTR
  • an HGLOBAL
  • CoTaskMem unmanaged memory

SecureString gives the lost ability back

In .NET, Strings cannot be wiped when you are done with them:

  • they are immutable; you cannot overwrite their contents
  • you cannot Dispose of them
  • their cleanup is at the mercy of the garbage collector

SecureString exists as a way to pass around strings safety, and be able to guarantee their cleanup when you need to.

You asked the question:

why can't I just say:

SecureString password = new SecureString("password");

Because now you have password in memory; with no way to wipe it. It's stuck there until the CLR happens to decide to re-use that memory. You've put us right back where we started; a running application with a password we can't get rid of, and where a memory dump (or Process Monitor) can see the password.

SecureString uses the Data Protection API to store the string encrypted in memory; that way the string will not exist in swapfiles, crash dumps, or even in the local variables window with a colleague looking over your should.

How do i read the password?

Then is the question: how do i interact with the string? You absolutely don't want a method like:

String connectionString = secureConnectionString.ToString()

because now you're right back where you started - a password you cannot get rid of. You want to force developers to handle the sensitive string correctly - so that it can be wiped from memory.

That is why .NET provides three handy helper functions to marshall a SecureString into a unmanaged memory:

  • SecureStringToBSTR (freed with ZeroFreeCoTaskMemUnicode)
  • SecureStringToCoTaskMemUnicode (freed with ZeroFreeCoTaskMemUnicode)
  • SecureStringToGlobalAllocUnicode (freed with ZeroFreeGlobalAllocUnicode)

You convert the string into an unmanaged memory blob, handle it, and then wipe it again.

Some APIs accept SecureStrings. For example in ADO.net 4.5 the SqlConnection.Credential takes a set SqlCredential:

SqlCredential cred = new SqlCredential(userid, password); //password is SecureString
SqlConnection conn = new SqlConnection(connectionString);
conn.Credential = cred;
conn.Open();

You can also change the password within a Connection String:

SqlConnection.ChangePassword(connectionString, cred, newPassword);

And there are a lot of places inside .NET where they continue to accept a plain String for compatibility purposes, then quickly turn around an put it into a SecureString.

How to put text into the SecureString?

This still leaves the problem:

How do i get a password into the SecureString in the first place?

This is the challenge, but the point is to get you thinking about security.

Sometimes the functionality is already provided for you. For example, the WPF PasswordBox control can return you the entered password as a SecureString directly:

PasswordBox.SecurePassword Property

Gets the password currently held by the PasswordBox as a SecureString.

This is helpful because everywhere you used to pass around a raw string, you now have the type system complaining that SecureString is incompatible with String. You want to go as long as possible before having to convert your SecureString back into regular string.

Converting a SecureString is easy enough:

  • SecureStringToBSTR
  • PtrToStringBSTR

as in:

private static string CreateString(SecureString secureString)
{
    IntPtr intPtr = IntPtr.Zero;
    if (secureString == null || secureString.Length == 0)
    {
        return string.Empty;
    }
    string result;
    try
    {
        intPtr = Marshal.SecureStringToBSTR(secureString);
        result = Marshal.PtrToStringBSTR(intPtr);
    }
    finally
    {
        if (intPtr != IntPtr.Zero)
        {
            Marshal.ZeroFreeBSTR(intPtr);
        }
    }
    return result;
}

They just really don't want you doing it.

But how do i get a string into a SecureString? Well what you need to do is stop having a password in a String in the first place. You needed to have it in something else. Even a Char[] array would be helpful.

That's when you can append each character and wipe the plaintext when you're done:

for (int i=0; i < PasswordArray.Length; i++)
{
   password.AppendChar(PasswordArray[i]);
   PasswordArray[i] = (Char)0;
}

You need your password stored in some memory that you can wipe. Load it into the SecureString from there.


tl;dr: SecureString exists to provide the equivalent of ZeroMemory.

Some people don't see the point in wiping the user's password from memory when a device is locked, or wiping wiping keystrokes from memory after they'authenticated. Those people do not use SecureString.


Edit: Don't use SecureString

Current guidance now says the class should not be used. The details can be found at this link: https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md

From the article:

DE0001: SecureString shouldn't be used

Motivation

  • The purpose of SecureString is to avoid having secrets stored in the process memory as plain text.
  • However, even on Windows, SecureString doesn't exist as an OS concept.
    • It just makes the window getting the plain text shorter; it doesn't fully prevent it as .NET still has to convert the string to a plain text representation.
    • The benefit is that the plain text representation doesn't hang around as an instance of System.String -- the lifetime of the native buffer is shorter.
  • The contents of the array is unencrypted except on .NET Framework.
    • In .NET Framework, the contents of the internal char array is encrypted. .NET doesn't support encryption in all environments, either due to missing APIs or key management issues.

Recommendation

Don't use SecureString for new code. When porting code to .NET Core, consider that the contents of the array are not encrypted in memory.

The general approach of dealing with credentials is to avoid them and instead rely on other means to authenticate, such as certificates or Windows authentication.

End Edit : Original Summary below

Lots of great answers; here’s a quick synopsis of what has been discussed.

Microsoft has implemented the SecureString class in an effort to provide better security with sensitive information (like credit cards, passwords, etc.). It automatically provides:

  • encryption (in case of memory dumps or page caching)
  • pinning in memory
  • ability to mark as read-only (to prevent any further modifications)
  • safe construction by NOT allowing a constant string to be passed in

Currently, SecureString is limited in use but expect better adoption in the future.

Based on this information, the constructor of the SecureString should not just take a string and slice it up to char array as having the string spelled out defeats the purpose of SecureString.

Additional info:

  • A post from the .NET Security blog talking about much the same as covered here.
  • And another one revisiting it and mentioning a tool that CAN dump the contents of the SecureString.

Edit: I found it tough to pick the best answer as there's good information in many; too bad there is no assisted answer options.