Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Dangerous to store a well hashed password and the method used to hash it in plain sight?

I am developing a client-side application in C# that will communicate with a server (php page) for credentials and for some critical services. I am wondering if it's dangerous to store a well hashed password on the client's machine? By "well hashed", I mean with a random seed using a well-known secure hashing function. For the purposes of this discussion, assume the source is freely available (as all binaries can be reverse engineered).

My thought was that I would store the username and hashed password on the user's computer and that this username and hash would be sent in plain text over an unencrypted http connection to the server for validation. This of course will not prevent a hacker from using someone else's username and password hash as their own without knowing the source password (with some code tweaks).

  1. Would a malevolent individual be able to do anything with a hashed password and code used to produce the hash? (other than log in as an other user if they obtained this information)
  2. What do other client-side applications do to prevent one user from logging in as an other user, if the hacker does not have access to the source password? (for example Steam)
  3. Is there an easy, cheap (both cost and time), more secure way to handle this?

Example of how a hacker would spoof someone else's login credentials using my current logic:

  1. Legit user signs in for the first time, credentials get stored
  2. Hacker gains access to the file system, locates username and hashed password
  3. Hacker modifies source code (or uses code injection) to send the acquired username and hashed password instead of what the program would normally do

The product I am producing is not going to be the next ebay, facebook, or stackexchange, and is low budget. I do not need something top notch, and don't care for thefts as I am planing to use a "Pay What You Want" model. I am mainly posting this for curiosity's sake.

like image 292
John McDonald Avatar asked Jan 25 '11 23:01

John McDonald


3 Answers

First off, let me just say that you are not competent to implement this system securely. I'm not either. Almost no one is. You need a security professional to do this work; if you try to roll your own security you will get it wrong and you will produce a system that can be compromised by attackers.

That out of the way: you have clearly identified the weakness of your system. You are storing a password equivalent; if an attacker obtains the password equivalent then it does not matter whether they have the password or not; they have information sufficient to impersonate the user.

The solution is clear. If it hurts when you do that, don't do that. Do not store a password equivalent if the system which stores it cannot be trusted to be robust against attack.

If you are hell bent on storing a password equivalent then store it in a storage which is more difficult for attackers to obtain access to than the file system. If you are really determined to store dangerous stuff on behalf of the user then you can use the Windows data protection API to store it in an encrypted storage that is encrypted with the user's credentials:

http://msdn.microsoft.com/en-us/library/ms995355.aspx

But better to not store the password equivalent in the first place.

Suppose you do not store the password equivalent. You are not done yet. You still need to consider replay attacks. Suppose the attacker eavesdrops upon the unsecured channel. The user types in their password, it is hashed and sent to the server. The eavesdropper records the hashed password, and now they have it.

To solve this, the server sends a random number to the client. The number will never be used again; it is one-time-only. The client xors the hashed password with the random number and then encrypts the result with the public key of the server. The encrypted xor'd message is sent to the server.

The server decrypts the hash using its private key, xors it with the random number it sent previously, and determines that the decrypted hash matches the password equivalent it is storing. Now the eavesdropper cannot replay the captured password equivalent because they will never see the same random number again.

Now, this is vulnerable to the man-in-the-middle attack if the client does not know the public key of the server! If the server sends the public key to the client then the man-in-the-middle can intercept the server's public key and replace it with his public key. Now the man-in-the-middle can provide the public key and the "random" challenge, and the client will give up the password equivalent.

Like I said, this is hard stuff. Don't try to do it yourself. Get a professional.

like image 122
Eric Lippert Avatar answered Oct 07 '22 13:10

Eric Lippert


Storing the hashed password on the user's computer exposes them to (considerable) risk -- even if access to your site isn't particularly valuable, many users will use the same user name and password for many sites. If any of them use the same (as you said, well known) hash function as you do, access to that data can give an attacker access to those other web sites.

For a simple, low-budget method, I'd use a simple challenge/response system. When the user asks to sign in, send them a random number. They enter their password into a client-side program, which hashes it, and uses the hashed password as a key to encrypt the random number, and then sends that result to the server. The server uses its stored hash of the user's password to encrypt the same random number. When it gets the result from the user, it compares the two. The user has the right password if and only if the two match.

If your intent is to avoid the user having to enter the password all the time, store the random number and encrypted random number on their machine. As long as they receive the same challenge, they can send the same response. When/if the challenge changes, they need to re-enter the password to encrypt the new challenge. Recovering the stored data gives the attacker access only to your site, and only for a fairly short period of time (and changing the challenge sooner is trivial as well).

This avoids transmitting the password (even in hashed form) at all. The only exception is during the initial setup, when you need to get the hashed password onto the server. For that, you have a couple of choices, but PK cryptography is likely the most obvious -- you send them a key and they send the data encrypted with that key. If somebody else intercepts the data, it's useless (unless they break encryption, of course).

As far as how to generate the random numbers, the usual way is pretty simple: generate a single really random number from a source like /dev/random on your server. Use (a hashed version of) that as a key to an encryption algorithm, which you run in counter mode - i.e. you have a counter (that you don't have to keep secret) that you encrypt with that key (which you do keep secret). Each time you need to produce a new challenge, you increment the counter, encrypt it, and send out the result.

like image 28
Jerry Coffin Avatar answered Oct 07 '22 12:10

Jerry Coffin


I deeply discourage trying to create your own authentication scheme. If you use time-tested patterns, it's more likely that edge cases you hadn't considered will be covered. Here's what I'd suggest and why:

  1. Don't allow logging in with a hash value instead of a full password. That way, even if an attacker acquires password hashes stored on the server, they won't be able to log in with them.

  2. Try to avoid storing passwords, even hashed passwords. If you want an easy login, @Mitch's suggestion is a good compromise, but I'd suggest you encrypt that expiring token and maybe tie the expiring token to some additional evidence (for example, the token is invalidated if it's sent using a different IP address).

  3. Never transmit credentials in plain text. Even if the transmitted credentials are hashed, if traffic is captured, a replay attack is possible. SSL isn't extremely difficult to implement, and it will give you the added bonus of the client being able to validate that they're talking to the right server, which helps protect against man-in-the-middle attacks.

  4. Never store a user's password on the server. Store a salted hash instead. Make sure the hash is a large hash value to reduce the likelihood of brute force attacks succeeding (SHA-256, for example).

I know you're trying to go for a cheap, easy-to-develop solution. Rather than implementing shoddy security to achieve that goal, try using pre-built, time-tested authentication libraries instead. That's a way to save time/money without compromising quality.

like image 26
Jacob Avatar answered Oct 07 '22 12:10

Jacob