Holy Smokes!

Thursday, March 25, 2010

Storing Passwords

I’ve never much considered myself a security expert, and I’ve rarely paid much attention to how much attention I’ve paid to things like storing passwords.

I’ve always had a ‘just protect the innocent’ when it comes to that stuff.

But, there comes a time when you have to step up to the plate, and learn the proper way of doing something.  And I’m starting to focus on, how to properly store passwords, for a scenario where an authentication system exists, which is exposed to the web.

You know; I guess I would normally look at it as, how important is the data that exists on the application?  Would someone want to take the time to obtain such passwords?

Then I realize, this isn't really the point.  Let’s not even consider the fact that people will often use the same password over and over, so the act of obtaining a password and an email address together could put an awful lot of risk.  Yes, I now believe that using a different password in different places is probably a good idea.

So, I looked into it, and I decided, why store the password?  Why not run that password though a Message Digest Algorithm, like MD5.  The thought was brilliant!  It works!  By not storing the password, I’ve essentially removed this burden.  See a stored hash does not contain any traces of the original password, and so this basically means that someone would have to have the hash, and start brute forcing the same algorithm until they have the same result.  right?

Wrong!

As I thought about all the hassle that someone would have to go through to generate possible passwords, it dawned on me, that by caching that which was generated, one would essentially be able to create a dictionary, whereby getting a hold of a hash, they would be able to come back very quickly with the original password, or, at the very least, a lucky collision, generating a sum that the application would accept.

I stopped here… to do some research.

Turns out, some nice folks on the internet (trillions and zillions of people) have already thought of this, and use an analogy called ‘salting’.  Forgive my improper use of terms, I’ve already stated I’m just learning. =)

So, what salting is, is taking something known, and appending it to the password before generating the hash.

See, in this case, even if a password is discovered that generates the hash in the database, entering that through the application will fail, simply because:

original password hash generated as:
     password_hash = hash(password + salt)

When discovered password is tried again
    password_hash = hash(password_and_salt + salt)

Well, that’s the theory anyways.  So I came up with a really redundant way of doing this, and I think the net effect would have been fine.

Although, I talked to a friend of mine, much more knowledgeable in the area. I immediately stated that I have no idea what I’m doing, but I have an interest to learn.  He points out my flaws.

First of all, I hard coded the salt, which is fine so long as it’s kept private, but once that salt is discovered, ALL passwords are compromised.

My reaction was, OK, so generate a random salt for each password, where do I store them?  They can’t be public!

Sure they can.  It takes a long time to generate a dictionary, and if the salt is large enough, and the passwords are moderately strong, the time and space requirements to generate these reverse lookup or rainbow (two very different things) tables makes it annoying, or very likely un feasible for a thief.

So I’m now sitting in a place, where I know a lot more about the topic.  I’m still not an expert, and I’m still VERY much willing to learn.  I’ll share my little test project with anyone willing to look, and very much welcome all criticism.  I figure it’s hard to troll someone who’s openly stating their desire to learn.  So, have at it!

I set a table up in SQL Server that looks like this

id -  int 
username - varchar(50)
password - binary(32)
salt - binary(64)





I set up 64 bytes for the salt.  Which is probably overkill.  Would very much be interested to hear how large you folks would use.  Maybe only 8 bytes?





I would use something like the following to generate salts and password hashes.



 /// <summary>
/// Password Encoding
/// </summary>
private static Encoding m_encoding;

/// <summary>
/// Random number generator
/// </summary>
private static Random m_random;

/// <summary>
/// Hash Provider
/// </summary>
private static SHA256 m_sha256;

/// <summary>
/// initialize static members
/// </summary>
static Security() {
m_encoding = Encoding.UTF8;
m_random = new Random();
m_sha256 = new SHA256Managed();
}

/// <summary>
/// Generate new random salt
/// </summary>
/// <returns></returns>
private byte[] new_salt() {
byte[] salt = new byte[64];
m_random.NextBytes(salt);
return salt;
}

/// <summary>
/// Generate Hash, in a more english form
/// </summary>
/// <param name="input">password to hash</param>
/// <param name="salt">must be binary, because no constraints to salt, doesnt
/// have to be characters, nor compatable with specified encoding</param>

/// <returns>literal hash</returns>
private string get_hash(string input, byte[] salt) {
StringBuilder sb = new StringBuilder();
foreach (byte b in get_hash(m_encoding.GetBytes(input), salt))
sb.Append(b.ToString("x2").ToLower());
return sb.ToString();
}

/// <summary>
/// Generate Hash
/// </summary>
/// <param name="input">decoded password</param>
/// <param name="salt">salt</param>
/// <returns>binary hash</returns>
private byte[] get_hash(byte[] input, byte[] salt) {
MemoryStream ms = new MemoryStream();
ms.Write(input, 0, input.Length);
ms.Write(salt, 0, salt.Length);
return m_sha256.ComputeHash(ms.ToArray());
}



Nothing too technically complicated.



When creating users, I would store the password like this:



byte[] salt = new_salt();
command.Parameters.AddWithValue("@username", username);
command.Parameters.AddWithValue("@password",
get_hash(m_encoding.GetBytes(password), salt));
command.Parameters.AddWithValue("@salt", salt);



So as you can see, we never actually store the password.  Once the password is entered, its hashed, and then forgotten.  Some might say that this is a weakness, especially for things like password recovery.  I say it’s fine, who wants a password they forgot anyways.  And if I were able to regenerate it, so could a thief.



Something to keep in mind here, to authenticate this, all we need is the salt.  And this is NOT a weakness, because generating the hash, is like a one way encryption.  I can take any piece of data, and generate a hash, but I cannot determine the data to get to a hash.



I would authenticate like this.



first obtain the salt.



SELECT salt FROM [users] WHERE [username] = @username



Next, use that salt to generate a new password hash, the same as we did when we created the user in the first case.



command.Parameters.AddWithValue("@username", username);
command.Parameters.AddWithValue("@password", get_hash(m_encoding.GetBytes(password), salt));


Then I would use this to hit the database again, with a scalar query.



SELECT ISNULL ( (SELECT 1 FROM [users] WHERE [username]=@username AND [password]=@password), 0)


The nice thing about doing it this way, is that I never pull anything related to the password into the application, just the salt, and just when authenticating. I would never store any of it in an object relational model of any sort. So.. someone grade me! Have I finally found a relatively sane way to store passwords? As a matter of fact, can someone tell me what the original password is here?



password hash:



0x50B0B41BF017A921730B950F8195AB9E7BA8FC525509B20851BFB12E1FB328E5



salt: 



0x724670D30F17831437162D383AC4DFC43BF9A02C9566D6126F7829636188A556
6614A4B0302FF3228A7A39403EDBE76FFF7086CC2701410B6F5D6497ED1FB3AF

2 comments:

  1. Hey Mike. Can't use .NET users and roles?

    ReplyDelete
  2. I guess, but that's not really the point...

    which I guess is just wanting to learn something.

    ReplyDelete

Followers