2

I have an API written in .NET Core 2.0 for dispensing JSON Web Tokens (JWTs). This is done via a POST request with a very simple model.

public class TokenRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}

The particulars of the API aren't important -- the goal here is to prevent the password from remaining in memory after the request is complete. Let's assume I can handle the string securely once it's in -- the problem I'm trying to solve is preventing Json.NET from assigning the value to a string and thus causing it to be interned.

EDIT: As pointed out in an answer the string is not interned here, which was my mistake. Even so, my concern is that the password could hang around in memory for a long time in a way that I can't control (a string) rather than a way I can control (a byte array, which I could zero out when I'm finished with it). I've removed further references to interning for clarity.

To test, I've been making requests and using WinDbg to determine if the values are in memory as strings.

My original model won't work as-is because it uses string -- even if nothing in my code references Password, just the fact that it's deserialized ends up putting the string in process memory until it's garbage collected.

Knowing that, I tried this model:

public class TokenRequest
{
    public string Username { get; set; }
    public byte[] Password { get; set; }
}

This actually works -- but the problem is that Password now must be a base64 encoded string. I couldn't find either the original base64 value or the decoded value in memory as a string, which was good, but having the password base64 encoded is undesirable.

I've looked at the source code of JsonTextReader and it makes sense why this works -- ReadAsBytes reads using char arrays and byte arrays, never strings. But unfortunately the base64 requirement is hard-coded in.

EDIT: Upon further consideration, the passwords are possibly still present in memory as byte arrays that haven't been GCed -- byte arrays I have no control over because they're internal to Json.NET

So, I tried a custom JsonConverter instead:

public class TokenRequest
{
    public string Username { get; set; }
    [JsonConverter(typeof(ByteConverter))]
    public byte[] Password { get; set; }
}

This suffers the same problem as the original -- Json.NET ends up sending the value through a string when parsing it for the converter, so even when the ReadJson method of my converter was nothing more than return new byte[] { };, the value could still be found in memory.

Summary

I'd like to get a plaintext string value into .NET Core WebAPI, and remove it from memory once the request is complete, without having to wait for garbage collection.

What kind of works, but is undesirable

  • Using byte[] (with the caveat it must be base64 encoded)

What doesn't work

  • Using any type of JsonConverter (Json.NET internally uses strings)
  • Using Ngen in any capacity (it might work, but it's not an option)
Jason E
  • 51
  • 5

1 Answers1

1

Strings created at runtime aren't interned unless something specifically asks them to be interned. I think this is perhaps just a confusion with what interning means. I think you are more likely just seeing memory that has not been garbage collected yet (or has and has not been reallocated yet).

You may want to look at this question: Securing a password input in c# dotnet core console app

As mentioned in some of this questions answers: You seem to be defending against a physical security issue not a code/data security issue. I've not used SecureString with json.net but I suspect that isn't going to address your concern.

Tninja
  • 11
  • 1
  • Good point about string interning. I thought I'd understood it well enough before posting but if not every string is interned then clearly it's not a concern that it will hang around in process memory _forever_. But, it seems that strings can hang around in memory a long time before being garbage collected -- that still seems undesirable for a password. – Jason E Feb 14 '18 at 01:42
  • @Tninja, can you elaborate more on why do you think SecureString won't help? – Dealdiane Feb 14 '18 at 02:29
  • @Dealdiane If the password is posted to a web service it's going to be in memory before you can stuff it in a SecureString. Plus you are not supposed to. "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." See https://msdn.microsoft.com/en-us/library/system.security.securestring(v=vs.110).aspx. – Tninja Feb 14 '18 at 06:15
  • SecureString would be sufficient, if only I could get it in without going through a string and keeping it in memory for an indeterminate period of time. I'm trying to limit the scope of potential harm, not eliminate it entirely. If clear text is used anywhere, there are always methods to get it. I'm not concerned about malicious attacks; those need to be handled differently. I'm trying to reduce the chance of a large number of passwords being accidentally exposed via a passive method like a memory dump. These are Active Directory accounts with access to a lot of different resources. – Jason E Feb 14 '18 at 12:42
  • @Tninja I see your point now. Wasn't looking at the whole picture especially the `input`. What if the string was encrypted and base64'd prior to the request? On receiving, you'd have to covert it to a byte array, decrypt it, and store each byte (char) to your SecureString. You still have the byte array in memory but is encrypted. – Dealdiane Feb 14 '18 at 20:24
  • @Jason-e If someone has enough access to get a memory dump you have a lot more to worry about than the passwords. I think I muddied the water by mentioning SecureString. Unless this code is running on some sort of shared hosting you have no control over I feel you are overthinking this. The best you will really get is security by obscurity which isn't really security. – Tninja Feb 14 '18 at 22:08
  • The problem from my perspective is that these are Active Directory accounts being authenticated, and the people who may have admin access to the server running this API (and thus are able to obtain a memory dump) aren't necessarily AD admins. Storing unencrypted AD passwords in memory seems like an unnecessary risk. Am I understanding correctly from this thread and the other linked one that current best practice for password security is that they can simply be stored unencrypted in memory, and no security precautions need to be taken in the code itself? – Jason E Feb 15 '18 at 00:05
  • I am not a security expert so I can't answer that definitively. If you don't trust the people who have access to this box you probably need to solve that problem with a non-code fix. I can say that where I work we have a nearly identical service that authenticates a user against our AD and issues a bearer token for our web applications to use. We don't do anything special to defend this. Maybe you would be better served asking the best practice question here or perhaps information security SO. – Tninja Feb 15 '18 at 19:50