0

Rate my authentication scheme? (This feels simple enough to be a duplicate, but it's really hard to search for these types of questions. Sorry if it is a duplicate.)


When a password is set, it's immediately hashed with a random salt S1 and stored as H1.

When a user tries to authenticate, they are sent S1 and a random value S2. They then hash their password with S1 to get H1, then hash that with S2 to get H2. H2 is then sent to the server.

The server simply computes H2 the same way, then compares the values. The server then generates a random 64-bit value C and sends it to the user.

All further communications with the server contain C as an authentication token.


The point here is to ensure that the password is never sent in cleartext; the communication channel between the user and server is assumed to be "reasonably" secure, in that this scheme is for an extremely low-value target; the user's most common passwords are likely to be the most valuable information present.

The protocol should defend against:

  • Determination of the password by a passive or active attacker
  • Impersonation of the user by a third-party without eavesdropping capabilities

EDITS

  • I'm not using TLS because this is for a video game; I'm trying to minimize latency as much as possible.
  • The user's password is set via a TLS-protected webapp.
  • I don't care (much ☺) about the NSA hijacking a user's account so much as 1337Kid__1 hijacking OtherPlayer's account, or an attacker intercepting passwords.
    • I'm making the assumption in the above that eavesdropping on another user's traffic is hard to do (for someone who isn't the NSA) without being on the same network segment as them; if this is a notion I should be disabused of, let me know.
Nathan Ringo
  • 207
  • 1
  • 6
  • This is the year 2016 (soon to become 2017). Just use SSL/TLS. It's overhead is trivial. – BlueWizard Dec 31 '16 at 16:55
  • For more on that subject, see https://istlsfastyet.com/ . That being said, I can see game situations in which TLS produces too much overhead, but those are probably where you're going to use UDP instead of TCP. – Xiong Chiamiov Dec 31 '16 at 17:06
  • Agreed, if you are using TCP, you are already introducing so much overhead that it is a little silly to balk at TLS. – NH. Jul 17 '17 at 13:09

3 Answers3

1

When a password is set, it's immediately hashed with a random salt S1 and stored as H1.

Hashed on the client side? If not, the password must be sent at least once in cleartext.

The point here is to ensure that the password is never sent in cleartext; the communication channel between the user and server is assumed to be "reasonably" secure,

Why not use encrypted traffic, such as TLS?

Rápli András
  • 2,124
  • 11
  • 24
  • Hashed on the server-side; it's set via a TLS-protected HTTP interface. I'm not using TLS because this is for a video game's communication; low latency is an extreme priority – Nathan Ringo Dec 31 '16 at 11:00
  • Have you tried it and experienced overhead? You could simply move on to UDP and use DTLS instead. – Rápli András Dec 31 '16 at 11:03
  • I'm creating the game right now; I'm already using UDP, but I haven't heard of DTLS before. There's an implementation for my language of choice, I'll check it out now. – Nathan Ringo Dec 31 '16 at 11:07
  • Okay, the Erlang implementation is of questionable quality (requires using [a fork of the language runtime last updated in 2013](https://github.com/RoadRunnr/otp/tree/new_crypto_dtls)), and [a project trying to implement WebRTC in Elixir](https://github.com/myers/exwebrtc) implies that as of 2014, no implementation existed. **EDIT** HAHAHA I CAN GOOGLE PROPERLY; the Erlang stdlib has [a dtls module](http://erldocs.com/17.4.1/ssl/dtls.html). The documentation is a bit lacking though... – Nathan Ringo Dec 31 '16 at 11:29
  • I guess you can use openssl too with erlang, openssl supports both tls and dtls. – Rápli András Dec 31 '16 at 11:37
1

It does not matter what the password is because in effect H1 is treated as the password, i.e. an attacker does not need to know the original password but H1 is enough. But this step at least ensures that the server does not see the original (weak?) password but a derived (strong) one and that same passwords are stored at the server with different H1 (if S1 is different) which makes brute-forcing the original password harder.

The way to get from H1 to H2 using S2 is similar to Digest authentication in that not the the password (or password equivalent, i.e. H1) is transferred but a hash of the password (or equivalent) and a server provided random nonce. The same advantages and disadvantages apply: it protects against sniffing (good) but the password or equivalent (H1) has to be stored in plain at the server. The last one is bad since somebody attacking the server could get access to H1 and use it directly to further authenticate as the original user. Contrary to this the usual way of storing hashed passwords need the attacker to find the original password which results in the stored hash.

The last step with sending a random C without further protection as the authentication token from the server to the client effectively nullifies all protection against sniffing you had so far since it is sufficient for the attacker to sniff this random token in order to hijack the session.

My suggestion is not to reinvent security but use instead well established and analyzed technologies like TLS to transfer plain passwords and then store the passwords in a safe way by using a suitable hash function.

I'm not using TLS because this is for a video game; I'm trying to minimize latency as much as possible.

I doubt that you authenticate the user again and again and that a small delay at the initial password authentication is a problem. But you need to protect the authentication token C against sniffing or reuse which you currently don't do. One way would be do never reuse the token but instead have an algorithm creating a new one all the time from a shared secret or use the shared secret in a HMAC over the payload.

Steffen Ullrich
  • 184,332
  • 29
  • 363
  • 424
  • What does the time overhead after authentication look like, though? And [an IETF presentation](https://www.ietf.org/proceedings/83/slides/slides-83-lwig-2.pdf) states that there's a significant amount of *space* overhead per datagram. (This might not be an issue, but it's a consideration) – Nathan Ringo Dec 31 '16 at 11:42
  • @tikiking1: with TLS you have the usual behavior of TCP which might be a problem when you have an application where data loss is fine but latency is essential (i.e. like real time audio). Apart from that there is some overhead with padding and framing but how much this is a problem depends on the size of the messages you exchange. After the initial handshake is done there is no delay apart from the one caused by TCP's reliability guarantee and congestion controls. – Steffen Ullrich Dec 31 '16 at 11:59
  • Oh, I meant for DTLS. I'm building a quick benchmark-ish thing into the testing client+server I have right now to see what the time overhead looks like. – Nathan Ringo Dec 31 '16 at 12:06
  • @tikiking1: DTLS is very similar to TLS in terms of size overhead. As for latency and reliability it is like UDP. – Steffen Ullrich Dec 31 '16 at 12:10
0

One nice aspect of /always/ doing hashing on the server side is that you will never have a timing attack. Naively comparing the server-stored password with the one coming from the network will reveal the password through the time it takes for authentication to fail (after multiple failures).

Hashing is not the only way to avoid a timing attack, of course - you could use a constant time comparison function. But as a guideline it's far safer to recommend hashing on the server too.

user134665
  • 31
  • 2