8
votes

I believe I'm misunderstanding a fundamental part of SecureString. I understand that string's are immutable and there a password or sensitive data is found as clear text on the heap.

What I'm struggling to understand is, how do I use SecureString in a client application which needs to verify the hashed password in a database?

Here's my context:

  • I'm using a WPF client application.
  • I have a local SQL database (on the client's machine)
  • The passwords are hashed and stored in the database.
  • The user tries logging into to my WPF application
  • The PasswordBox control stores the password in a SecureString via the SecurePassword property.

Now what? How do I hash a SecureString WITHOUT casting it back into string first?

All the advise I've received so far is to write extension methods converting SecureString to String, hash it and then send it to db to verify. But this defeats the whole exercise!

Must I just accept that SecureString is useless in my mentioned context and use plain string?

1
Personally, I find very little use for SecureString - they add very little security to an application for all of the effort they take to actually use properly. At the end of the day, a truly dedicated attacker will get your password because it must be stored in memory somewhere... the idea that SecureStrings will make it harder is true, but if an attacker is already looking at memory then I assume they're dedicated enough to get past it anyways. - Alastair Campbell
Let the database deal with the password. Send the username and password to a stored procedure, which will return whether the authentication was successful. You can deal with all of the encryption stuff in the procedure. - Mike Eason
@MikeEason Send password as cleartext to a stored procedure? Never! - Sami Kuhmonen
I want to make a warning / addition to this as it is dependend to some details. for example you should a secure connection. otherwise "everyone" could read the password from the network stream. If he is usin secure strings its indicating that he wants to secure his passwords ;) @Niels have a look at my answer - Boas Enkler
@MikeEason Passwords must not be encrypted, since that would suggest they can be decrypted. Rather they must be hashed with a one-way function. Then this is stored in the database and the user-submitted value is also hashed and these are compared. - Sami Kuhmonen

1 Answers

4
votes

SecureString is represented as a byte[] you could encode the bytes e.g. with bitconverter and save the result. Furthermore SecureString is a encryption not a hash as it can be decrypted. (see below)

SecureString mainly meant to store sensitive data in memory. If you have a service / website, this is not as important as the values which are stored in the database. These should never be plaintext, and imo not be decryptable by your or any administrator Also i'm not sure wether another server could decrypt the strings, so you may have a problem when you change the server or have somekind of cluster scenario.

Especially for passwords would prefer using hash algorithms (e.g. SHA256) . These can't be uncrypted (like the sum of the digits). In the use case of a login funtionality you would encrypt the userinput and compare the hashs the user and and the one thats in the database. (details se below) I would also suggest to add a dynamic criteria like the userid to the hashinput so that 2 user with same password would have different hashes.

With this strategy you don't have a risc with userpasswords and therefore if data gets leaked it wouldn't be a problem at this point.

Here an short overview of using hash algorithms

So ( if the securestring is given) first decrypt the SecureString

String SecureStringToString(SecureString value){
  IntPtr valuePtr = IntPtr.Zero;
  try{
    valuePtr = Marshal.SecureStringToGlobalAllocUnicode(value);
    return Marshal.PtrToStringUni(valuePtr);
  }
  finally{
    Marshal.ZeroFreeGlobalAllocUnicode(valuePtr);
  }
}

Than hash it for example with SHA256. From this post

  using (SHA256 hash = SHA256Managed.Create()) {
    Encoding enc = Encoding.UTF8;

    //the user id is the salt. 
    //So 2 users with same password have different hashes. 
    //For example if someone knows his own hash he can't see who has same password
    string input = userInput+userId;
    Byte[] result = hash.ComputeHash(enc.GetBytes(input));

    foreach (Byte b in result)
      Sb.Append(b.ToString("x2")); //You could also use other encodingslike BASE64 
  }

Store this hashsum. Depending on your encoding it may looke like this:

ac5b208b4d35ec79fa7c14b7a31f9c80392cdab2bc58bc5b79bcfe64b044d899

in your database. If the user signs on then create the hash from his input and compare it with the hash in the database. if they are equal then the password is correct. Therefore you never need to have the plaintext user password anywhere stored.

If the client makes the hash then it should absolut should no where exist as a plaintext (except the textbox if it doesnt support the securestring)

PS: this is only one option. But the main thing is to never store plaintextpasswords anywhere. For best never know them and have no change to get them decrypted.

Another strategy would be to use asymmetric encryptions like RSA but this can become more complex. If you need help with that i would recommend a dedicated post on this.

Depending on your requirements and envionment most of the time hashsums should be an acceptable solution. (But thats not an legal advice as i'm not a lawyer)