Skip to main content
GameDev.net gamedev.net
🔒 Locked

Plaintext passwords

Started by Butabee Jun 20, 2010 at 8:44 PM 14 replies 2.3k views
Original Post
Butabee
Butabee
Is it really that big of a deal if I store passwords as plaintext?

if it is can anyone recommend a good free encryption library?
hplus0603
hplus0603
If you store passwords as plaintext, then the first time your server gets hacked, those of your users who use the same password/email in more than one place will be compromised.

Generally, you want to store the hash of some challenge/salt and the password. Then you send a challenge to the client, have it calculate a hash of the cleartext password, and send it back, and compare to that.

The point of a salt is to make it so that two users with the same password won't have the same hash value in the database.

store: hmac(username + password, sha256)

to login:
rand = high_quality_random_16_bytes
send_to_client(rand)
client computes: hmac(rand + hmac(username + password, sha256), sha256)
server verifies that, indeed, the correct value is returned

Note that when issuing a challenge, you want to store the challenge and the time it was issued in a database somewhere, and not allow logins using challenges that are old, to avoid replay attacks.

If you don't know what hmac or sha256 is, I suggest you use the google :-)


Whatever you do, don't have a "secret word" that allows me to recover or change my password. A 8-character password with uppercase, lowercase, digits, and 5 different punctuation characters to choose from gives a 1:513798374428641 chance of guessing it. When the challenge question is "what is your favorite color?" you have a 1:10 chance of guessing right...

For password reset, mail a link to the user's e-mail address on file, including some un-guessable reset token. Do not actually reset the password until the link is clicked. When you reset, generate a new password randomly, apply it, and mail the password back to the e-mail address. This reduces the likelihood of a denial-of-service attack against your users by allowing third parties to randomly reset their passwords just because they know their e-mail addresses.
enum Bool { True, False, FileNotFound };
Butabee
Butabee
Thanks, I'm clueless about cryptography, so I found this encryption library Crypto++, and it has this hmac function, but I'm not sure what to pass as all the arguments.

HMAC(pbKey, nKeyLen)).CalculateDigest(pbOutputBuffer, pbData, nDataLen);

The only one I really know is the output buffer.
hplus0603
hplus0603
Crypto++ is a great library! It also has great documentation.

If you don't understand how buffers, pointers and lengths work, though, you are probably not at the point where network programming in C++ is something you should be doing yet. You have to master the basics of the language first, before you start delving into things like distributed systems.
enum Bool { True, False, FileNotFound };
Butabee
Butabee
I know how pointers and buffers work, I just don't know what data they want in the buffers for this function.
Butabee
Butabee
Ok, so I think I figured it out.

they key would be the random 16 bytes sent to the client, and the data is the username + password.
Butabee
Butabee
Wait, wouldn't the hash already be unique if it's the hash of username + password, since no two usernames can be the same?

I'm thinking I'd only have to run hmac on the server, the question is where would I get the key since I'm planning on distributing the server too, I guess I'd just have the user come up with a pass/key for their server they'd enter at startup. The problem is though, if the user running the server forgot the key, no one would be able to log in lol.
Atrix256
Atrix256
I believe the username is the salt that hplus was talking about so yeah (:

but hey btw, it's still possible to have 2 username/password combos result in the same hash.

It may be UNLIKELY, but it's still possible.

just somethin to keep in mind :P

Hodgman
Hodgman
You add a random number into the mix for each login so that if anyone is listening to the communication channel, the data they intercept will be useless in the future.
In other words, because the random number is different each time you try to log-in, the bytes you have to send to the server also changes each log-in (therefore an intercepted log-in packet can't be re-used).

If you're sending the same bytes to log-in every time, then a man-in-the-middle can easily steal your account, so it's a nice idea to add the random number ;)

[Edited by - Hodgman on June 21, 2010 12:25:19 AM]
Butabee
Butabee
Ok, I guess I will do it the way hplus said, I'm still a little confused as to what the argument buffers should contain.

I guess username + password for both pbKey and pbData?
PureBlackSin
PureBlackSin
I have spent some time thinking about encryption, some of the best stuff is done using blowfish 441bit encryption keeps data, safe, secure, but fairly easy to process, i think the encryption is better than military standard so fairly safe.

I think it has a nice c++ interface, as well as others (languages).

I second the send password reset to e-mail, it can be staggered and back off can be programmed in to stop denial of service and any near brute force attack.

Anyway, there's my 2p.

PureBlackSin
For Games, Articles and Custom High End Computers, come visit.Myndoko
hplus0603
hplus0603
It's fine to use the random number ("challenge") for pbkey, and the username/password for pbdata. The problem with this is that the password must then be stored in cleartext to calculate the proper hash. However, because of the HMAC, you don't need to send the cleartext password over the wire, which is at least one step of security.

Another option is to use a known key ("1234567890abcdef" for example), and calculate two hmacs the way I suggested:

1) A = key, username+password (stored in database)
2) B = key, challenge+A (calculated for verification)

Server generates, remembers, and sends challenge to client.
Client computes B and returns to server.
Server also computes B based on the challenge that it issued and compares to what the client returned.

Note that the key doesn't have to be secret (it could even be 000000000), because it's the password you're verifying. And, in fact, the key won't be secret, because it has to be built into your client for it to properly calculate the hash.
enum Bool { True, False, FileNotFound };
Butabee
Butabee
Thanks for explaining.

I already put it together the way you said before with username + password for both pbkey and pbdata.

EDIT- I was just thinking about it, and it's still kind of like putting the password in cleartext with the way I'm doing it.

I'm sending the hmac of random bytes + hmac of username + pass to the server.

But I'm not decrypting it on the other end, instead I'm just comparing it to the hmac of random bytes + stored hash(hmac of username + pass).

If someone got the hash, they could still steal the account.
hplus0603
hplus0603
Quote:
some of the best stuff is done using blowfish 441bit encryption keeps data, safe, secure, but fairly easy to process, i think the encryption is better than military standard so fairly safe


Blowfish was updated to a more modern algorithm and submitted as a candidate for the AES encryption algorithm selection contest. However, another algorithm (Rijndael) won, because it was better. I highly, highly suggest that you use AES for encryption if you need a symmetric cypher. It's had the most research done to it and is still standing up. Blowfish is, at this point, pretty old.

Quote:
If someone got the hash, they could still steal the account.


The point is that you only allow the hash of the random challenge once. If someone tried to re-use the hash, you'd notice that that challenge was not "the current challenge" and the hash would not work.

A decent way of implementing this is to "mint" a challenge using a decent random source (Mersenne Twister seeded by /dev/random, say) when the client first attempts to connect, and store this challenge relative to the client name (the client has to supply the name as part of the connection attempt). Also store a time at which the challenge is issued.

When you get back the hmac(challenge+hmac(name+password)), you verify that not too much time has elapsed (5 seconds?) and that the hash is valid, then you immediately remove the stored challenge. That way, nobody else can re-use the same challenge/hash. If the hash doesn't match (user typo-ed the password, say), you should issue a new challenge anyway, as part of the "password didn't match" message.

If you really, really worry about someone sniffing the challenge and somehow beating the user to the punch, then you can also issue a token that consists of a timestamp, the IP you know the client has, the client ID, and a secret key that only the server knows. hmac(time+client_ip+username+secret_key). The client simply returns this token to you together with the hash of the challenge. You can then verify that too much time hasn't passed, that the IP address is the same, and that the user ID is the same. This means that someone who sniffs the token and breaks the return message to the server with the password hash, still can't re-use the stolen hash, because he/she doesn't have the same IP address.

If you worry about someone who can insert themselves seamlessly in the middle of the conversation, then you need to set up a public-key cryptosystem where, as part of installation or whatever, the user generates a public/private key pair, and the server, similarly, uses a public/private key pair. As long as the private parts of the keys don't leak, and nobody can spoof your client to believe that some other key is the server key, you can then protect against such a man in the middle. Typically, by generating a random, strong key for use by a symmetric cypher, as the public/private key cyphers are a lot slower. The SSL secure sockets mechanism also has a similar mechanism -- you'll generally do well to use an existing library, such as OpenSSL or Apache or Windows or whatever, if you want to use secure sockets. You do get into a whole other can of worms of certificate/key management, though.

Not to mention that it's much more likely that your users' password leaks through a keyboard sniffing trojan, or a post-it note taped to the monitor, or someone asking them in chat "hey, my password is pretty secret, it's "qwerty"! what's yours, it is as good?" ;-)
enum Bool { True, False, FileNotFound };
Butabee
Butabee
What I'm doing now, is just not allowing someone with the one username to log in more than once, so even if someone else sent the same hash they'd get an already logged in message.

I'm deleting the challenge once a user get's disconnected.

Doesn't this work pretty much the same?

I don't think I'm gonna worry about super tight security with public/private keys or tokens and all that.
hplus0603
hplus0603
That will probably work well enough, assuming your detection of multiple logins, and your detection of disconnected users, is reliable.
enum Bool { True, False, FileNotFound };

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.