Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How To Properly Handle Passwords In C#

It's a well known fact that C# string is pretty insecure, it's not pinned in RAM, the Garbage Collector can move it, copy it, leave multiple traces of it in RAM and the RAM can be swapped and be available as a file to be read, not mentioning several other known facts.

In order to mitigate this Microsoft came up with SecureString. The thing is: what is the proper way of using it?

I ask this because sooner or later you will have to extract the inner string. And yes, SecureString does allow us to write one char at a time and gives us some other minor usages while hiding it's secret, but sometimes we need the whole string at once or something like it. So far all implementations I see use a regular string at the end, with the extracted and decrypted content from the SecureString which I think (or hope) it's avoidable in some cases even though it might involve some complicated code to get around a .NET string.

In my case I use a library for password hashing called BCrypt. It uses a regular string for calculating the hashes, which I think is far from ideal. I didn't find any libraries that would accept a SecureString directly, so I don't have much of an option but use it.

Currently I run code such as this:

SecureString password; // This usually comes from a PasswordBox Property
IntPtr passwordBSTR = Marshal.SecureStringToBSTR(password);

string insecurePassword = Marshal.PtrToStringBSTR(passwordBSTR);
Marshal.ZeroFreeBSTR(passwordBSTR);
passwordBSTR = default(IntPtr);
password.Clear();

// Use the insecurePassword....usually as:
bool result = BCrypt.Net.BCrypt.Verify(insecurePassword, user.Password); // This hashes the provided password and compares it with another BCrypt hash.

insecureString = string.Empty; // hoping for the GC to act now, fingers crossed.

// use result and etc...

This way the password raw string will be copied multiple times for the BCrypt method and probably even more times inside it.

This makes some questions arise in my mind:

1 - Are BSTR strings like C strings in the sense of memory management? Are they pinned in RAM and I can manipulate them and erase them as I see fit so it will be more secure to use them in C# or interop it with a C++ code so I can eliminate a lot of my .NET string manipulations (like verifying if the password string is null or white space or has the correct length or has the desired password strength)?

2 - If question's #1 answer is "yes" then should I invest some time to pass the BSTR string to a interop C++ code to calculate it's hash and verify?

3 - Is my later code correct or am I missing something on SecureString manipulation?

Just to be clear: My main intent with this question is to evaluate if I should pass all my password handling code to C++ from my C# (probably using C++/CLI or interop) to achieve a more secure code than what C# has to offer. This scenario really concerns me for password string hashing were multiple strings will be created and copied with the user's secret information scattered all over the RAM.

As an example: I could make a method like:

public string SecureHash(SecureString password)

And this method will extract the SecureString value and pass it as a BSTR string (or other extraction option, I honestly don't know the benefits between them) to a C++ hashing method, so we can control all the places our password sits in memory.

I ask this to not have a huge work refactoring my code and in the end discover it was all for nothing because I was using SecureStrings wrongly all the time and there are already solutions for this, or a false sense of security, as it happens sometimes, since I am not aware of how BSTR strings are implemented in memory or the other SecureStrings extraction options (Unicode, ANSI and etc).

like image 849
Michel Feinstein Avatar asked Sep 22 '16 20:09

Michel Feinstein


1 Answers

  1. Marshal.SecureStringToBSTR() will create a BSTR string (i.e. an array of bytes) in unmanaged memory and return a pointer to it; it means the underlying value is pinned. The value itself is a sequence of Unicode characters representing your string, prefixed with string length, and followed by two subsequent null characters (https://msdn.microsoft.com/en-us/library/windows/desktop/ms221069(v=vs.85).aspx). On the other hand, Marshal.PtrToStringBSTR() will return a .Net System.String object, which is insecure.
  2. I would say your options are:

    • Convert your SecureString object to unmanaged object and then pass to any unmanaged library which calculates hashes
    • Use SecureString everywhere in your stack, in your case it means to rewrite BCrypt.Net to work with SecureString objects (the library is open source and really small: https://bcrypt.codeplex.com/SourceControl/latest#BCrypt.Net/BCrypt.cs), or find an alternative managed implementation. But since SecureString doesn't provide methods to read its data, you will need to convert it to, most likely unmanaged, string anyway at some point (you can use that unmanaged data from your managed code though).
  3. You're missing correct disposal of SecureString (either within "using" or try/finally).

like image 75
johnnyjob Avatar answered Oct 04 '22 00:10

johnnyjob