Note: phiên bản Tiếng Việt của bài này ở link dưới.

https://duongnt.com/securestring-vie

Use SecureString to store secrets in C#

A lot has been written about how to store passwords. But an often forgotten question is how to securely read passwords from user input and store them in memory. In old .NET Framework code projects, we sometimes encounter the SecureString type. This is one way the .NET team tried to keep the secret confidential.

Why we shouldn’t store a secret as a String object?

Below is a simple program to read user input and store it into a string object. It seems simple enough.

private static void ReadString()
{
    Console.Write("Please enter your password: ");
    var password = Console.ReadLine();
    Console.WriteLine();
}

Read password into a string

However, when we use a string object to store a secret, we can’t control its memory lifetime. As we know, in C# .NET a string is a reference type, which means its memory is allocated on the heap, and it is subjected to garbage collection. We don’t know when (or even if) that memory will be reclaimed.

We will dump the memory of this program with dotnet-dump and try to recover the secret with WinDbg. This is the command to read all string objects from the dump.

.foreach (address  {!DumpHeap -type System.String -short }) {du ${address}+c }

When executed on our memory dump, it returns the following output.

Recover the string from memory

What if we use a char array?

Maybe we can modify our code a little to read each key press separately and append them into a StringBuilder object? This means there won’t be any string to be recovered from memory, will there?

private static void ReadChars()
{
    Console.Write("Please enter your password: ");
    var sb = new StringBuilder();
    ConsoleKeyInfo key;
    do
    {
        key = Console.ReadKey();
        if (key.Key != ConsoleKey.Enter)
        {
            sb.Append(key.KeyChar);
        }
    }
    while (key.Key != ConsoleKey.Enter);
    Console.WriteLine();
}

Unfortunately, in this case the char array can still be recovered from the memory dump. First, we use this command to get the addresses of all char arrays in the dump.

!DumpHeap -type System.Char -short

Char array address

Then we can use this command to read the content of all those arrays.

db <address>

Char array content

Although a little harder to read, we can still clearly see the secret.

Let’s switch to using SecureString

One important thing to keep in mind while using SecureString is that we mustn’t store its contents in any temp string object. After all, a temp string still lives in memory and has a lifetime we can’t control. The code below is from Microsoft document. This is one way to read a secret from input and store it into a SecureString.

private static void SecureString()
{
    Console.Write("Please enter your password: ");
    using var securePassword = new SecureString();
    ConsoleKeyInfo key;
    do
    {
        key = Console.ReadKey();
        if (key.Key != ConsoleKey.Enter)
        {
            securePassword.AppendChar(key.KeyChar);
        }
    }
    while (key.Key != ConsoleKey.Enter);
    Console.WriteLine();
}

SecureString input

Let’s find the addresses of all char arrays this time.

SecureString char array address

Next, let’s examine their contents.

SecureString char array content

As we can see, there is no char array that stores our secret. But what about the SecureString object itself? We will locate its address in the dump.

!DumpHeap -type System.Security.SecureString -short

SecureString object address

This address also doesn’t store our secret.

SecureString object content

Should we use SecureString in new projects?

Unfortunately, it is not recommended to use SecureString in new projects. You can find the detailed reason in this link. Below are some takeaway points.

  • While SecureString reduces the time we store the secret in plain text, it does not completely eliminate this risk. Because .NET still needs to get the secret from input somehow.
  • SecureString relies on encryption to keep the secret confidential. But this is not supported in all environments.

Instead, we should take advantage of certificates or Windows authentication…

Conclusion

I became interested in SecureString after seeing it in an old repository. Turns out this is no longer the recommended solution to keep secrets confidential. But I still had fun playing with its memory dump.

A software developer from Vietnam and is currently living in Japan.

One Thought on “Use SecureString to store secrets in C#”

Leave a Reply