2

I'm updating a C# project to make it more secure (I got very strict requirements for it). One of the requirement is to protect some data in memory to prevent "unauthorized" access to it. I made my research and ended up with "ProtectedMemory.Protect". I'm not expert in C#, so I'm not sure if doing it right or not. A simplified example how I implemented it:

public class User
{
    public string Name
    { 
        get 
        { 
            return Encoding.UTF8.GetString(ProtectedMemory.Unprotect(_name, MemoryProtectionScope.SameLogon)); 
        }
        set 
        { 
            _name =  Encoding.UTF8.GetBytes(value);
            ProtectedMemory.Protect(_name, MemoryProtectionScope.SameLogon);
        }
    }

    private byte[] _name;
}

Is this makes sense at all? :D

Gabor
  • 33
  • 6
  • I read in the [docs](https://learn.microsoft.com/en-us/dotnet/api/system.security.cryptography.protectedmemory.protect?view=netframework-4.8) that `The userData parameter must be 16 bytes in length or a multiple of 16 bytes` – Phate01 Mar 19 '20 at 09:54
  • 5
    It is very hard to do this securely in .NET, due to the garbage collector being free to copy memory whenever it wants if this is necessary to compact heaps. You get memory management "for free", but as a result it becomes very hard to manage it explicitly (which you need if you want to make sure unencrypted copies of data aren't floating around). It's generally more practical to focus efforts on securing the machine itself instead: processes can't effectively shield their memory, but you should make every effort to keep attackers from being able to read memory in the first place. – Jeroen Mostert Mar 19 '20 at 09:54
  • 2
    What is the *real* requirement? Protect from access by **whom exactly**? It's not another process - that kind of protection is ensured by the OS. Prevent access by a *debugger*? That requires elevated privileges on the box, and if one can do that, they can monitor the code that actually puts data into the "protected" block. Prevent the data from appearing in memory dumps? You'd need extra privileges to generate a dump too. – Panagiotis Kanavos Mar 19 '20 at 10:01
  • Phate01: yes, I'm converting my string, I just simplified the code for my question. Jeroen Mostert: I know, that I can't make a bulletproof solution, just want to make it hard enough for other applications to access my data. – Gabor Mar 19 '20 at 10:02
  • 3
    As for this code explicitly: no, this makes no sense. Every access of the property will create a brand new `string` instance with an unprotected copy of the data that will not be effectively erased (except coincidentally if memory movement overwrites it), making the protection of the `byte[]` a bit pointless. Every `set` call likewise involves passing in a `string` that is itself already unprotected; protecting an encoded copy is closing the barn door after the horse has bolted. This is the `SecureString` problem all over again. – Jeroen Mostert Mar 19 '20 at 10:03
  • 2
    @Gabor that's not a solution at all. First, **no other process can read your memory** unless it's a debugger. And a debugger can just check the variables before you put them in there. What are the **actual attacks** you want to protect against? There *are* ways to mitigate or prevent them – Panagiotis Kanavos Mar 19 '20 at 10:04
  • You want to prevent debuggers? Ensure no account on that box has debugging rights. You want to protect the data at all times, like passwords? Use and store hashes, so the data itself is never available. There are other solutions for different attacks – Panagiotis Kanavos Mar 19 '20 at 10:11
  • @Panagiotis Kanavos - I think who wrote the requirements wasn't sure what they want (regulatory body), just going crazy with cyber security. The requirement is: "Protect data in memory to prevent other applications to access it (in runtime and in the future)." This is shortened, but in a nutshell this is what they want. I'm encrypting it when the data is stored, but at some point I need to show it to the user, so it gets into the memory decrypted. – Gabor Mar 19 '20 at 10:28
  • @PanagiotisKanavos I *guess* *if* say we need to send a memory dump to someone to analyze a bug, we don't want to send memory dump with user's password in there. That *might* be case why we need to encrypt data and keep it safe. Sometimes ourselves could be the attackers? But again, there's no 100% way to secure data in memory if it has to be a string. – weichch Mar 19 '20 at 10:30
  • @Gabor *In the future* sounds dangerous. You don't know what attackers will be using to hack your app in 5 years time :) Just kidding. – weichch Mar 19 '20 at 10:33
  • It's easy enough for someone to jot down a sentence that's supposed to make things more secure if implemented correctly, and another thing altogether to actually implement it. If you don't want security theater, get someone who knows both regulatory requirements *and* technology to evaluate the requirements and develop a strategy. Such a strategy may well involve "we can't use .NET for this bit since protecting memory is too hard". The code you have now ironically makes things *less* secure than if you just had *one* unencrypted string in memory, since now you'll end up with *many*... – Jeroen Mostert Mar 19 '20 at 10:38
  • ...and that's in no way intended to challenge your competence, just to point out how hard security is to get right unless you really treat it as its own subject. It's not merely a code or language problem. – Jeroen Mostert Mar 19 '20 at 10:38
  • @weichch: stored data, memory dump. With the "future" I meant when my application is closed. – Gabor Mar 19 '20 at 10:43
  • @Gabor the only solution to that is to not use password authentication. Use Windows authentication, OAuth or some other mechanism that removes the need for passwords. – Panagiotis Kanavos Mar 19 '20 at 10:45
  • @PanagiotisKanavos Yes, and considering the % of applications using *password* authentication compared to those ones *not*. It is almost no point to protect *password* once hit the memory. However, all of those are technically correct, what about making the product people happy part? We should at least give them something they want :) – weichch Mar 19 '20 at 10:49
  • @Gabor *what data is this*? When the app is closed, there's no memory to read. If you have passwords, don't store them at all. Hash them properly. Or use authentication that doesn't need passwords. Different attacks require different techniques and technologies. – Panagiotis Kanavos Mar 19 '20 at 10:49
  • @JeroenMostert I know that security is a hard question and this is the first time that someone requested this level of security. Every other security requirement is filled, this is the only one which drives me crazy, because I have no clue how to achieve it. – Gabor Mar 19 '20 at 10:49
  • @Gabor What level of security are we talking about? If it is just tick something off the requirements list, then any security mechanism can do right? However, if it has to be indeed secure like you will likely have security guys reviewing / hunting in your app for problems, then it's a different story. – weichch Mar 19 '20 at 10:53
  • @PanagiotisKanavos authentication is solved, but I'm working with patient data and medical records, what I need to protect in every possible way. I solved everything else what they asked for, this is the last part what they want (or a good explanation why I don't need this) – Gabor Mar 19 '20 at 10:54
  • 1
    I would achieve it by proving/certifying that on your machine there are literally no other applications capable of reading your application's memory, and none can be introduced (because admin access is tightly controlled and admin actions are audited), and no memory dumps can escape from it. In other words, loop it back to securing the machine, which is the sensible thing to do in the first place. When you get down to it, only the operating system can help you actually protect memory (not merely minimize the window where unencrypted data could be read, which is all an application can do). – Jeroen Mostert Mar 19 '20 at 10:55
  • Related: [Is a SecureString ever practical in a c# application?](https://stackoverflow.com/questions/26190938/is-securestring-ever-practical-in-a-c-sharp-application) – John Wu May 01 '21 at 08:20

2 Answers2

1

Moving the protected memory from a private backing field to a property will not help as now one only needs to read a public property, and the memory protected by the class ProtectedMemory can be read by any application that injected in your assembly and run in your process, there are a lot of ways to do that!

I guess that's why MS stoped with secure string and ProtectedMemory in the newer frameworks as it really doesn't make much of a difference to a skilled attacker or some fool with the right tools...

What else... well one imported thing to note is that this is you need to clean up any memory that contains GDPR related data asap as one can use Heap inspection to find confidential data.

Basically what you need to do is use encryption and only make data accessible when you need it and clear it as soon as you do not.

As to your problem, here is one way I solve it using my Walter.Cypher nuget package. I used your class added a salad and assumed you would like to prevent anyone else using my NuGet package from reading your data by "padding the password".

using System;
using System.Text;

public class User
{
    public User(int id, string name)
    {
        Id = id;
        _name = name.Encrypt(
            string.Concat(Id, "APPLICAT10N PA$$wo0rd").AsShaHash(HashMethod.SHA1, 8) //use a salt based key protect the user and the application
            , Walter.Cypher.CipherMethod.Medium //use a 365 bit hash
            );
    }
    public int Id { get; }


    private byte[] _name;

    internal string GetName()
    {
        return  _name.Decrypt(
            string.Concat(Id, "APPLICAT10N PA$$wo0rd").AsShaHash(HashMethod.SHA1, 8) //use a salt based key protect the user and the application
            , Walter.Cypher.CipherMethod.Medium //use a 365 bit hash
            );
    }
}


public class UserViewModel : IDisposable
{
    User _user;
    private Lazy<string> _userName;
    private bool _disposedValue;

    public UserViewModel(User user)
    {
        _user = user;
        _userName =new Lazy<string>(_user.GetName());
    }

    public string Name 
    {
        get => _userName.Value;  
    }

    protected virtual void Dispose(bool disposing)
    {
        if (!_disposedValue)
        {
            if (disposing)
            {
                //do what you need to do
            }

            _userName = null;
            _disposedValue = true;
        }
    }

        
    ~UserViewModel()
    {
        Dispose(disposing: false);
    }

    public void Dispose()
    {

        Dispose(disposing: true);
        GC.SuppressFinalize(this);
    }
}

This code will work on all platforms as it does not rely on any window API but asymmetric encrtiption.

Whenever you feel the need to use a property to store confidential data, make sure you understand that that is a security issue.

Walter Verhoeven
  • 3,867
  • 27
  • 36
0

If you are doing ASP.NET Core applications, take a look at data protection: https://learn.microsoft.com/en-us/aspnet/core/security/data-protection/using-data-protection?view=aspnetcore-3.1

Turns out below is deprecated. Thanks for pointing that out.

Otherwise, if you are allowed to use unsafe code, check out SecureString here.

weichch
  • 9,306
  • 1
  • 13
  • 25
  • 2
    There's a bit of confusion here. `SecureString` isn't unsafe, it's pure .NET but it's *not* that secure - it prevents data from appearing in dumps, but won't prevent a debugger from intercepting the calls that put data into that secure string. Plus, the data has to come from *somewhere*, which is *not* protected. User input? Available on the wire, or as clear text until it's written into that protected memory. File input? Data from a database? – Panagiotis Kanavos Mar 19 '20 at 10:03
  • Data protection protects *data*, not the memory too. – Panagiotis Kanavos Mar 19 '20 at 10:03
  • As per what'd been asked in the question `One of the requirement is to protect some data in memory` I assume *data* is the right thing to protect? – weichch Mar 19 '20 at 10:05
  • No, it's not. The *data* has to be available in the clear in memory before it can be encrypted, or after it's decrypted. Data protection only allows you to encrypt/decrypt data for storage. It doesn't prevent access to the decrypted data – Panagiotis Kanavos Mar 19 '20 at 10:09
  • The Data Protection APIs protect *data at rest* by encrypting it using keys specific to the machine or account, so other machines or other accounts can't decrypt them. – Panagiotis Kanavos Mar 19 '20 at 10:13
  • Yeah I know. The whole point of protecting data in memory is to avoid clear text being read. There is no secure way to make sure when the data is being used, it is still secure. If something has to be 100% secure secure, then it should never be used in memory as a string. – weichch Mar 19 '20 at 10:25