Advertisement

User Encryption / Authentication

Started by August 26, 2024 02:01 PM
5 comments, last by hplus0603 1 week ago

I'm about to put username and login into my app. I have everything working but I'm having a hard time tracking this info:
When creating a new user/logging in, are you sending their raw username+password across the internet. Do you use some reversible hash in which the data gets encrypted+sent and then decrypted on the server so that it can get the raw password and hash it? I'm not using HTTP protocol (maybe I will), but it sounds like HTTP would already encrypt data and unencrypt behind my back so if I'm using a web server script I would access the raw password already when accessing data.

If I don't use HTTP, then what reversible hash is recommended, and do I need to add any salt to it? Would it be a standard salt value for every user, or how do I get the server and user on the same page as to what salt is being using to encrypt data coming over the internet?

NBA2K, Madden, Maneater, Killing Floor, Sims

don't use reversible hashes. client-side hashing is barely better than plain text. use TLS, be it HTTPS or your custom protocol. i would strongly recommend using HTTP over a custom protocol. (full disclosure: i am a web developer; your mileage may vary.)

When creating a new user/logging in, are you sending their raw username+password across the internet

yes. using TLS, this is a non-issue. you can get a free SSL certificate from letsencrypt, you just need to prove ownership over your domain name.

HTTP would already encrypt data and unencrypt...

no, HTTP does not.

regarding HTTP and a custom protocol, think of HTTP has plain-text metadata envelope around your payload. if not HTTP, you would be writing _some sort_ of message envelope in your custom protocol anyway. http authorization overview. bearer authorization is very common, next to session cookie. libcurl is the de facto standard HTTP client library.

gluing the HTTP authentication to the client's TCP/UDP connection could look something like:

  • client negotiates with authentication server to obtain an authentication token over HTTPS
  • client negotiates with game server using authentication token
  • the game server independently verifies authentication token is valid
  • the game server associates the user to the TCP/UDP session

if I'm using a web server script I would access the raw password already when accessing data

correct. check if your language of choice has a secure string option; scrub its memory contents once it's been used.

which reversible hash is recommended, and do I need to add any salt to it?

your questions are pointed at the transport of sensitive data, but i'll supplement it with password storage.

never use a reversible hash. a hashed password must never be reversed. take a look at bcrypt. when a user tries to login, the server should hash the requested password.

e.g. use `hash_alg(requested_password) == target_user.password_hash` and never `requested_password == unhash(target_user.password_hash)`.

Advertisement

Do you use some reversible hash in which the data gets encrypted+sent and then decrypted on the server

@fastcall22 is correct, you use TLS for this. It is a well-designed and well-tested reversible hash (a k a “encryption”) system.

If you already have HTTPS then that's probably the easiest to do, but you can also roll your own on top of openssl or gnutls or your OS TLS library if that's more your jam. Or you can use DTLS over UDP.

Also, you'll need to figure out where to get a proper TLS certificate. If you embed a TLS library in your client, you can make your own root certificate and use that to sign the server certificate, and then ship the root verification certificate to the client, and it can verify that the server matches the certificate you embedded in the client.

If you use HTTPS and use built-in system HTTPS libraries, you'll need to get an actual globally trusted SSL certificate. If you host on the big clouds (AWS, GCP, etc) then they have certificate managers that can do this for you for free. Else you can use something like letsencrypt if you don't want to pay for a certificate.

You always send the cleartext password to the server, because this is the authenticator. The transport security protects it. On the server, you typically run a “hard hash” like argon2 to hash the clear-text password, and then validate against the previously-stored hash. This has the benefit that it's expensive to try to brute-force if someone gets a copy of the hash, and if your users table leaks, only the hash, not a reversible password, is leaked.

If you really want to roll your own, you could generate private/public key pair using some cryptosystem (like Ed25519) and then embed the public key in the client. As long as the private key is not leaked, you can encrypt the password with that public key on the client, and then decrypt it on the server, without having to use a TLS certificate. However, there's a bunch of other attacks that TLS defends against, so I wouldn't recommend this approach for anything real.

enum Bool { True, False, FileNotFound };

If the server is a webserver, can I just HTTPS to a .php url? The last I did something like this, I was using mobile phone app that invoked a php file to query a database, update database and send back information from the database.

NBA2K, Madden, Maneater, Killing Floor, Sims

Yes they work, but there is nuance there.

HTTPS is designed to allow a trusted MITM attack, such as a corporate network or school network that the machine has a trust relationship with. In that regard, since anybody can establish a trust relationship with any other machine it isn't secure in the sense that an attacker can completely observe that portion of the payload on machines they control. Attackers can use the endpoints freely, and can DDOS them, look for exploits on them, and build their own clients that mimic legitimate communications patterns. The endpoints and commands aren't secret in that regard, but it is generally enough security for authentication to establish a session. It is secure enough as non-trusted machines generally can't eavesdrop and the communications are tamper resistant.

The use of a GET request with the field and value parameters on the URL or a POST request with the field and values in the body won't make a difference to security. As long as you have an HTTPS connection you'll get that level of security built in. Use it to establish your session. As you're using a stateless web request you'll need to continue using a session ticket that gets sent as a parameter to all future calls, they should automatically expire after a short time, with an API call to renew session tickets and replace them.

As for the database, NEVER store those passwords in a recoverable format. Always use a library like bcrypt and follow the documentation instructions to include a salt value. The thing you store is the hash of (password + salt). Even if an attacker gets a copy of the database they still can't do a brute force attack on the hashes because of the salt parameter.

Passing between servers (such as an auth server and a separate game server) also needs a way to establish a security triangle. Clients should be able to request a security token to give to a game server, and a game server should be able to ask the auth server if a security token is a valid token for a player. That completes a chain auth servergame clientgame serverauth server so your game server can validate the player is authorized to use the service.

There are several major systems that use HTTPS calls. Two are the Steamworks API documentation and PlayFab API documentation you might find useful to study.

dpadam450 said:

If the server is a webserver, can I just HTTPS to a .php url?

Short answer: Yes!

Long answer: See frob's additional cautions, and in general, there are many different kinds of attack, and HTTPS just defends against some of them.

frob said:
Always use a library like bcrypt

Bcrypt is still not broken, and the general idea of storing hash(password+salt) (or even better hash(password+salt+pepper)) is correct.

However, bcrypt is considered “old, and legacy,” and the current recommended best choice is argon2.

Another best practice is to require a minimum password length of 16. (Lastpass used to default to 14 for “generate new password” but that's not strong enough.) You don't need to require special characters of any kind, but you should require that the password is not the same as the email address / phone number, and contains at least 5 different characters.

(And, as a user, I highly recommend the “four words” style passwords, which some generators like Bitwarden can generate and remember. They are easier to transcribe when you read the password on your phone but have to enter it with a game controller on a console… But you-the-server-developer don't have as much control over that.)

enum Bool { True, False, FileNotFound };
Advertisement