🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

User subscription and protected passwords.

Started by
8 comments, last by hplus0603 7 years, 4 months ago

I had watched a short clip on youtube where they talked about encrypting a password on the client side and then sending it over to the server for storage when a user registers for an account. Then every time after that if the user wants to log back in, the log in function encrypts the password before sending it off to the server. If the encrypted versions are the same then the user is allowed access to the site. So now I was thinking about how I would go about encrypting something sufficiently. This is my proposed method.

You'll have to be patient with me as I am an intermediate JavaScript programmer.

User enters proposed password, minimum 8 characters long, for sake of simplicity only letters and numbers are allowed.

example: mypassword88

First step, all letters are converted into numbers

modified version: 1325160119192315180488

second step, password is broken up into chunks of 4

modified version: 1325 1601 1919 2315 1804 88

then organised by ascending order.

modified version: 1235 0116 1199 1235 0148 0088

then parts are destroyed based on position, underlined numbers are destroyed

modified version: 1235 0116 1199 1235 0148 0088

new version: 235 016 119 123 148 088

then parts are copied, underlined numbers are copied

modified version: 235 016 119 123 148 088

new version: 2352 0161 1199 1231 1484 0888

then new encrypted? password is made, if following numbers are not within the alphabet then the number remains.

encrypted password: x5tpks9l3k484h88

then that is what gets stored in the server and I don't have to know what the original password was :D

what do you guys think, for the level of encryption I need I think this will suffice, thoughts?

Advertisement

That isn't really "encrypted".

The current general standard is to use bcrypt so you store the computed hash of the password and not something that allows any degree of recovering the password.

Alternatively, use a hash (like SHA1) of the password with a salt value so you never need to know the real password.

It looks like you've already spent more brainpower than would have been required to use one of the above.

Rule #1 of security: never never never NEVER never invent your own encryption or hash functions. _never_

Secure encryption functions take teams of experienced cryptologists years to design and then go through years of public feedback and verification.

And yes, games demand the highest security possible. Anything that suits just passwords requires that, because users are dumb and use the same passwords in multiple places, so if your game server is compromised then you are partially responsible for leaking tons of users secrets.

Always always send passwords over encrypted channels (e.g. use SSL/TLS). Always store them as salted high quality cryptographic hashes.

No exceptions.

(edit: correct a few phone autocorrects, both hilarious and mundane.)

Sean Middleditch – Game Systems Engineer – Join my team!

If you just encrypt on the client side, then someone who can "snag" the "encrypted" password, on the client, or on the way to the server, will be able to use that (without knowing the unencrypted password) to gain access, because the server doesn't see anything other than the "encrypted" password.

Whatever YouTube video you were watching is either dangerous snake oil, or it was talking about things in a context that's not the same context as you're using.

There are only two "secure" ways of authenticating with passwords:

1) The most commonly used, and what I would recommend:

- On initial password creation, server calculates scrypt(password) (or a similarly modern key function like bcrypt(password)), and stores this result in the database. The result includes information internal to scrypt/bcrypt, such as "number of rounds" and "salt used" (salt is random for each password.)

- On login, user provides username and cleartext password (presumably over TLS / HTTPS) and server asks bcrypt to validate that cleartext password + data from scrypt matches. Because data from scrypt includes salt and rounds, the library can do this. Once validated the server overwrites the memory that stored the cleartext password, and just knows whether it passed or failed. Additional short-term credentials, like a login cookie, may be issued.

2) The alternative, which is more secure in some ways (especially over non-secure channels) but less secure to server intrusion:

- On initial password creation, server stores cleartext password in database (presumably with some reversible encryption.) This is dangerous because if someone can dump your user database, they may be able to easily recover the password.

- On login, the server sends a random "challenge" to the user and remembers this challenge for a small amount of time. The user calculates HMAC(challenge + cleartext password) and returns to server. Server then calculates HMAC(challenge + database password) and compares. If the values match, the login is accepted, the challenge is forgotten, and short-term credentials are issued.

Any variation on these methods is almost 100% certain to weaken or totally defeat the security of login. These are the best practices for a reason!

enum Bool { True, False, FileNotFound };

so if you were to run a hash & salting on a password would it be better to do it on client side before it's sent over or on server side before it's stored in the database? Or how about for s&*t's and giggles a unique function on both client and server side before storage?

Salted Password Hashing - Doing it Right has a section on just this:

In a Web Application, always hash on the server

If you are writing a web application, you might wonder where to hash. Should the password be hashed in the user's browser with JavaScript, or should it be sent to the server "in the clear" and hashed there? […]

The problem is that the client-side hash logically becomes the user's password. […]

  • Client-side password hashing is not a substitute for HTTPS (SSL/TLS).
  • You need to salt the client-side hashes too. The obvious solution is to make the client-side script ask the server for the user's salt. Don't do that, because it lets the bad guys check if a username is valid without knowing the password.

Just to re–iterate the others in this thread, sending a password in cleartext is not an issue with a secure connection. No secret transformation of the password is necessary before sending it.

The approach is well-intended, but seriously flawed in several ways (and not advisable).

First, this is a form of hashing, but the hash function is very poor. Normally, a hash function mixes all bits in the input in a non-obvious way to produce a (shorter) output, and (for cryptographic hashes) it is unfeasible/impossible to reverse the operation or to guess another input which produces the same output etc etc.
The hash that you describe simply drops some letters at fixed positions, which is very bad. This further reduces entropy which is already notoriously bad in user-chosen passwords, which actually makes cracking passwords a lot easier.

Second, client-side hashing is something you can do, but it is not by any means sufficient by itself to provide any level of security. A hashed password is still only a plaintext password. Unreadable to humans, yes, but still just a plaintext password. It can be intercepted as-is (on the client, or on the wire), and sent to the server, and the server will not notice. Client-side hashing somewhat adds to security insofar as it adds work to online brute force attacks (which are however very rare!), and it makes the input to your symmetric encryption more random-looking which may make attacks on the cipher more difficult (although a cipher that isn't considered broken should already be resilient to patterns in its input -- this was, however, an argument against using counter mode for well over a decade!). Arguably, a tiny amount of security is also added insofar as the iterations done on the client are extra work that an offline attacker successfully having cracked the password database must also do in order to retrieve the real, human-readable password (users tend to reuse the same password on several websites/services!).

However, to provide reasonable (not perfect) security, hashing (with salt) must be done on the server, regardless of whether it is also done on the client. The by far biggest threat is an offline attack on the database following an intrusion. The main thing to defend against is therefore a massively parallel offline attack which compromises not one, but all user passwords in a short timeframe. This must be as difficult to do as possible. The more difficult, the more time you have to warn your users about it (so they change passwords) once you discover an intrusion has happened.

Given the unearthly speed at which hashes can be calculated on GPUs nowadays, usually not a single hash is done, but many thousand iterations, or a specially crafted memory intensive mix function. Several well-researched implementations exist for that application, one was mentioned earlier (bcrypt).

If you just encrypt on the client side, then someone who can "snag" the "encrypted" password, on the client, or on the way to the server, will be able to use that (without knowing the unencrypted password) to gain access, because the server doesn't see anything other than the "encrypted" password.

...

There are only two "secure" ways of authenticating with passwords:

..On login, user provides username and cleartext password (presumably over TLS / HTTPS) and server

Your first objection is just down to assuming that the "bad" system doesn't use TLS/HTTPS, though, right? Sending cleartext to server and having it do the hashing would have the same man-in-the-middle/snooper problems without HTTPS...

In an apples-to-apples comparison, is there any issue in doing the hashing on the client side? If you use a secure hash that actually takes a second to compute, that's could be a lot of cloud server costs that you've eliminated by moving onto the client, and it gives complete peace of mind in any kind of compromised-server situation that you can't possibly have cleartext passwords stolen from you because you've never possessed them (even temporarily). I imagine that that's a plus, given things like heartbleed have highlighted the idea of even transient data being stolen from you without being aware :(

You need to salt the client-side hashes too. The obvious solution is to make the client-side script ask the server for the user's salt. Don't do that, because it lets the bad guys check if a username is valid without knowing the password.
There's plenty of other solutions -- e.g. the salt can be a transformation of the username. Even if this is done on the server, it can return a valid result for non-existent usernames.

Or if it is the traditional server-generated random number scheme, the server can create new table rows for invalid user names, generating and storing a salt for them but not actually creating an account obviously -- which avoids leaking knowledge of whether a username is valid or not to the client... etc, etc.

I assume that allowing the client to know the salt of any one particular user isn't a security issue anyway / doesn't weaken anything?

so if you were to run a hash & salting on a password would it be better to do it on client side before it's sent over or on server side before it's stored in the database?

My post explicitly talked about this. If you just hash and salt on the client, the only thing the server has to go on is the "encrypted" password, and thus anyone who can access your server and dump your user database can immediately log in as any of your users.

In an apples-to-apples comparison, is there any issue in doing the hashing on the client side?

See above. 99% of the time that we see an "intrusion" in the media, it's all about the intruders getting access to the customer database, with user names, email addresses, and whatever-you-store-as-password. This is why storing password hashes where the server is responsible for enforcing strength and checking validity is so important.

The golden rule is: What's stored in the database must not be what is sent in the login request (be it cleartext or hashed) and must not allow easy recovery of what's sent in the login request.

Best practice is: Don't hash on the client side. It's really that simple.

enum Bool { True, False, FileNotFound };

Your first objection is just down to assuming that the "bad" system doesn't use TLS/HTTPS, though, right? Sending cleartext to server and having it do the hashing would have the same man-in-the-middle/snooper problems without HTTPS...

There are some protocols to authenticate via a shared secret known to the server and client, allowing for secure login without TLS. Rough overview is that the client and server communicate some computed values from the password and they'll only match up if the password is known on both ends, but the computed secret itself tells you nothing. A nonce (one-time-value) is used to defeat replay attacks. Server takes secret and a random number, computes number, sends both to client. Client takes secret and provided number, computes same secret, verifies it, computes another number from inputs, sends that to server. Server verifies new number to authenticate the client.

There are two problems with this approach:

(a) you have to get the secret onto the server in the first place, which puts you right back to needing TLS. but if you only allow signup on a Web page or something, that might be easier for you. e.g., use TLS on the Web server for signup, then use raw data on subsequent game client connections.

(b) the secret aka password has to be shared, meaning known in full to both the server and client, so the server can't just store a hash of the secret. however, this is the exact case where having the client compute a (salted) hash to send to the server for storage works. hashing a password that's used as a secret into a more complex authentication protocol is just fine, so long as the original hash is cryptographically secure. Note that the initial signup still needs to be TLS protected because the hashed password is still a _secret_.

All that said, such approaches are silly wastes of time. If you have the Web site, just make a login endpoint that works with one-time session keys.

User opens client to login. Client sends username/password over TLS to a login HTTP service. Service authenticates the client and authorizes a new session, then generate the one-time session key, and sends that to the client. Client connects to game server and hands over the key. Game server sends the key to the session authentication service, which verifies that the key is valid and no more than ~30 seconds old or so, then marks the key as used, and returns the user's identity to the game server.

Note that this is what (good) games _do anyway_ even if they have TLS! You don't want to sling the user's password off to every server endpoint you have. Limit the password interaction to the client and login endpoint so you have fewer places where the password could be snooped (e.g., if someone hacks your maybe-easily-hacked native C game server and logs all incoming requests to a remote server, you don't want that to be usable to steal passwords). This is also incidentally the core idea behind authentication standards such as OAuth2. Number #2 rule of security - manage risks (have as few of them as possible)!

That _still_ leaves a problem, though: the client's established connection can be easily hijacked/snooped. If it's just a cooperative or unranked pvp game, no big (but then, why bother with logins in the first place?). If you have a ranked pvp game - or if you have any way of expending consumables or in-game currency via the game protocol - then this hijacking could spell trouble (I could cheat to hurt competitors, or make them drain their account's wallet of in-game gold, etc.).

What's the solution there?

You guessed it: use TLS/DTLS _for everything_. Every single packet. In and out of game. Encrypt all the things. :)

Games that need very high throughput or low-latency still work just fine with encryption today. Encrypt/decrypt network packets off the main game thread on the client side, and (if you are big enough to afford them) use a TLS endpoint on the server end (or at least just also keep the encryption work off the main server thread). The days of encryption being too expensive for real-time games is well behind us.

Sean Middleditch – Game Systems Engineer – Join my team!

This topic is closed to new replies.

Advertisement