Protect sensitive data in-memory using C#

1.1k Views Asked by At

I have a C# application which initializes some variables with sensitive data. They are not password, but we consider them sensitive though. The variable values I want to protect are all of string type.

What I am trying to do is to find a mechanism (I know there is not 100% mechanism) that allows me to protect in-memory variable values or at least to make it a little bit more difficult to the attackers to read them. So I can protect sensitive data in-memory of my application and make it more "unreadable" by any other application that can trace, dump or read my application at runtime.

I see that exist the Microsoft Data Protection API (DPAPI), which has a class called ProtectedMemory which offers two methods protect and unprotect, here is an example.

Anyway it looks like MS stopped use ProtectedMemory as some guy is saying here since it is not useful.

Other people here instead suggest to hash them with a good function (e.g. SHA1/2) and store the digest.

I would like to use private properties (as it is only used within my class) instead of a simple variable which its set property encrypts it into memory and the get property, decrypts it. As below:

public static MyClass
{
    private string MyVariable
    { 
        get 
        { 
            return Encoding.UTF8.GetString(ProtectedMemory.Unprotect(_myVariable, MemoryProtectionScope.SameLogon)); 
        }
        set 
        { 
            _myVariable =  Encoding.UTF8.GetBytes(value);
            ProtectedMemory.Protect(_myVariable, MemoryProtectionScope.SameLogon);
        }
    }

    private byte[] _myVariable;
}

And the same for the rest of my string variables.

I wanted to do the same as this guy in his post but some people there say moving the protected memory from a private backing field to a property is not going to help (i am not sure what he is referring to, if using a property is not being protected¿?). Could you also confirm me if using a private property for my variables is going to be protected in memory or not?

So how can I do this? could somebody provide a simple code snippet? The encryption/decryption process should be relatively fast.

I do not want to use any third-party, only OS help and .NET Framework.

1

There are 1 best solutions below

3
Emperor Eto On

.NET offers SecureString for this ostensible purpose. But it comes with a huge caveat that all but destroys its utility:

Very few .NET APIs support it. This means that you would need to convert between SecureString and string many times in the lifecycle of your application. Every time this is done is an opportunity for the secret value to leak into unencrypted RAM. You have to be extremely meticulous therefore in wiping plaintext copies of the string when they're no longer needed by using unsafe code. Plus, you don't know how many copies of your secret will be created when passing that string to other APIs. Take JSON serialization for example - if you need to put your SecureString's unencrypted value in a JSON stream, that value could easily exist somewhere else in RAM, possibly many times over, after JSON serialization is finished. Same goes for HTTP headers and other places where secrets are commonly exchanged.

With all that said, if you want to take the plunge, you'll need to be aware of how to convert from a SecureString to a normal string for the 99.99% of .NET APIs that don't support it:

    public static string ToInsecureString(this SecureString str)
    {
        if (str == null)
            return null;
        if (str.Length == 0)
            return "";            

        IntPtr unmanagedString = IntPtr.Zero;
        try
        {
            unmanagedString = SecureStringMarshal.SecureStringToGlobalAllocUnicode(str);
            return Marshal.PtrToStringUni(unmanagedString);
        }
        finally
        {
            Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString);
        }
    }

and how to convert a normal string to a SecureString:

    public static SecureString ToSecureString(this string s, bool readOnly = true)
    {
        if (string.IsNullOrEmpty(s))
            return null;
        unsafe
        {
            fixed (char* chars = s)
            {
                var ss = new SecureString(chars, s.Length);
                if (readOnly)
                    ss.MakeReadOnly();
                return ss;
            }
        }
    }

And finally, you need a way to zero-out the bytes of a plaintext string when you're done with it:

    public static void ScrubString(ref string s)
    {
        if (s == null)
            return;
        unsafe
        {
            fixed (char* chars = s)
            {
                for (int i = 0; i < s.Length; i++)
                {
                    chars[i] = (char)0;
                }
            }
        }
    }

And you have to be EXTREMELY careful with that last one; due to the way .NET caches strings, this could have more consequences than you'd expect. You should never use this except on plaintext strings converted from SecureString using the ToInsecureString above, since you know those will exist in their own buffer.

Is all this worth it? Honestly probably not. It's almost always a losing battle to try to keep secrets protected in-RAM. But if you want to try it, or just need to check a box on an audit form, then go for it.