3

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.

Willy
  • 9,848
  • 22
  • 141
  • 284
  • 1
    You are talking about hashes and decrypting. Hashing is a one-way operation. There is no decryption. The problematic part is not that its a property but that at some point the data to be protected is a string. There are optimizations for string that make it hard to get them out of memory. So whatever you do your secret (at best) should never be a string. Not even intermediate. You shown class has that problem. Its as string in memory so it will stay in memory you can't erase that. – Ralf Apr 03 '23 at 15:47
  • If your problem is that people can dump process memory and read secrets, your problem actually started well before this point. If you want to keep secrets, keep them on machines that *you own and control*. Or are you going to solve a problem that the entire games industry failed to be able to until they could do that? – Damien_The_Unbeliever Apr 03 '23 at 15:59
  • Making a variable `private` has absolutely no value for this purpose. Reading the memory directly while it's active is possible, but also not usually a concern. Instead, what commonly happens instead is an attacker will run a program designed to cause a blue screen crash dump, and then the sensitive memory is written to disk as a matter of course. This is _much worse_, because now the attacker can attempt to brute force decryption keys over a period of time. When it comes down to it, if you need the variable as a string, **it's exposed**, and _there is no work-around_. – Joel Coehoorn Apr 03 '23 at 16:20

1 Answers1

2

.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.

Emperor Eto
  • 2,456
  • 2
  • 18
  • 32
  • 1
    Just an another adition about securestring: [DE0001: SecureString shouldn't be used](https://github.com/dotnet/platform-compat/blob/master/docs/DE0001.md). 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. – Vadim Martynov Apr 03 '23 at 20:43
  • 2
    And also from an official documentation: "A SecureString object should never be constructed from a String, because the sensitive data is already subject to the memory persistence consequences of the immutable String class. The best way to construct a SecureString object is from a character-at-a-time unmanaged source, such as the Console.ReadKey method." – Vadim Martynov Apr 03 '23 at 20:45
  • 1
    Thank you @VadimMartynov! I wasn't aware MS had officially confirmed what I'd suspected for awhile. And of course the recommendation you quoted rather proves my point that `SecureString` is all but useless without jumping through enormous hoops. – Emperor Eto Apr 03 '23 at 20:56