1

I asked a related question earlier, but I want to ask a broader version this time:

To simplify things let's say I've developed an application that allows users to send messages in a chat room, and to each other (similar to IRC). The nature of the message doesn't require too much secrecy, nothing sensitive is ever shared.

I develop an open-source server and an open-source client for the application. Users will setup their own servers, I simply provide the server and client programs.

I want users to have accounts on each server. The client should ideally log on with a username and a password.

I can assume the client won't steal account information because the user got it from a trusted source. The problem is the server can't be trusted because:

  1. Each server's owner can make modifications to their server's code.
  2. They can see the contents of any database.

How can I authenticate users:

  1. without letting the server owner access the user's password
  2. without letting the server owner access a precursor to a user's password (anything that isn't the password, but can either be turned into the users password, or used to log into another server).
  3. without relying on an precomputed key that has to stay hidden on either the client or server (the source code would reveal it).
  4. ideally allowing a password Edit: Ideally the password and username would be all it'd take to log on, so logging on with different machines the user would only need the password and username.

The approaches I have in mind:

  1. Challenge-Response: Send a challenge, hash it with a password, and send it as a response. This passes requirement numbers 1 and 3, but fails the 2nd, as it requires the server to also have access to the hashed password, which can be turned into the actual password, or used to log onto other servers (the latter is slightly lesser, since it can be dealt with if it occurs). My other question goes into details on how I implemented this idea for testing.

    2: Server Whitelist: This feels a little like cheating, but it meets all the requirements by "removing" the biggest issue, the fact the server can't be trusted. If the client tries to connect to a server not on the whitelist, a strongly worded warning would be issued(or access to non whitelisted servers could be completely blocked). This has the downside of complicating the process of users creating new servers, and it places more responsibility on me than simply developing the application (I'd have to manage the whitelist). It also doesn't necessarily ensure that the server can be trusted since even if the owner isn't malicious, there's no guarantee they have proper security on their systems.

Selali Adobor
  • 184
  • 1
  • 7
  • You could use certificates + password-encrypted private keys, if the latter doesn't count as "a precursor to a user's password". Otherwise, the only way to satisfy 2&3 is to involve a third party, as is part of Andy Boura's suggestion of Federated Identity. –  Aug 10 '14 at 07:26

4 Answers4

3

Use certificates. That way the client never shares it's private key with anyone including the server. The server just has the clients public key which isn't secret.

Alternatively run a federated identity solution or leverage one such as Google or Facebook's. There are open source options for this (such as http://en.m.wikipedia.org/wiki/OpenAM ) You also can't be entirely sure the client hasn't been tampered with but in your scenario that doesn't sound like a problem anyway.

Andy Boura
  • 759
  • 3
  • 10
2

It sounds like you want public-key cryptography similar to what SSH supports. The basic idea is that the user provides their public key to the server during the account creation process, then proves their ownership of the corresponding private key during the login process.

The public key, as the name implies, isn't even remotely secret, so it meets requirement 1, it can't be used to construct the private key, so it meets 2, it's computed at account-creation time, so it meets 3, and yes, you can use a password to protect the private key.

Mark
  • 34,390
  • 9
  • 85
  • 134
  • I like this approach, the only problem is it seems man in the middle attacks become trivial without an encrypted connection. Is this true for all forms of public-key cryptography? – Selali Adobor Aug 10 '14 at 06:31
  • Also it seems to complicate the concept of accounts. The account would now be tied to a key, but if the user goes to another computer the key won't be present. – Selali Adobor Aug 10 '14 at 06:34
  • What do you expect the man in the middle to be doing? Some things are trivial (eg. impersonating a server), while some are impossible (stealing login credentials by impersonating a server). – Mark Aug 10 '14 at 07:20
  • Yeah, I was worried about impersonating a server, but the nature of the program makes impersonating a server fairly pointless. – Selali Adobor Aug 10 '14 at 07:28
  • Would it be acceptable to use a KDF-based algorithm to turn a passphrase into a private key? Then use that to generate the public key? – Selali Adobor Aug 10 '14 at 07:56
  • No, because a private key needs certain mathematical properties (what those properties are depends on your choice of algorithm). – Mark Aug 10 '14 at 08:01
  • So it's pretty much a requirement that the user move the private key around? – Selali Adobor Aug 10 '14 at 08:02
  • Yes. It's not that hard -- it's just an ordinary file, and you can add UI to your client to make handling it easier. – Mark Aug 10 '14 at 08:03
  • I'm afraid with the "casual nature" of the program this might prove to be a major problem. The problem is convincing users to carry a key around for a casual program is a little difficult. And users might be tempted to start doing seemingly "unsafe" things, like hosting their keys on insecure mediums. – Selali Adobor Aug 10 '14 at 08:15
  • I'm also thinking might implement a centralized, secure server to host user private keys to act as a middle-man for the insecure servers. Is that an intrinsically flawed approach? – Selali Adobor Aug 10 '14 at 08:18
  • If you're going to do that, you might as well go all the way and write a centralized authentication server (or add support for an existing one). – Mark Aug 10 '14 at 09:09
2

Any solution is going to involve some public key cryptography. Each user can have a key pair. The secret key could be encrypted with a password as suggested by @Mark.

If it is for some reason not practical to store a private key, and the user must be able to log in using just the software and a password, the private key can be generated on the fly.

You can have a database containing the following for each user:

  1. User data

  2. A salt for the user's password

  3. A salt for the public key

  4. A hash of the public key (Made with the above salt)

To authenticate:

  1. The password salt is sent to the client.

  2. The client combines the password salt and the password and use this combination to seed a PRNG.

  3. The output from the PRNG is used as input for any private key generation algorithm you choose (RSA or any other signature algorithm).

  4. The public key is then sent from the client to the server.

  5. The client must also prove that it holds the corresponding private key (this could be done by signing a session identification or through a sigma protocol).

  6. Once the server knows that the client has the secret key, it validates the public key by hashing the public key with the stored salt and compares the result to the hash stored in the database.

The order of the last two steps was chosen to prevent an attacker who somehow learned public key (but not private key) from learning if that public key could be used to access a particular account.

No iterated hash is needed, as the algorithm to generate the key pair tend to be sufficiently computationally intensive. Unlike the iterated hash, the key pair computation does serve the secondary purpose of keeping the secret information well hidden from the server.

If the existence of a user account must be concealed, the server can simulate the protocol when a non-existing username is provided as follows:

  • In the first step of authentication generate a pseudorandom password salt and send this to the client.

  • Follow the protocol through all the following steps not depending on the database.

  • In the last step of authentication compute a hash and discard the result. Reject the user regardless of the value of the hash.

The remaining challenge in concealing the existence of a user account is how to produce a psuedorandom password that changes at a frequency, which is indistinguishable from the frequency of password changes for a real user.

kasperd
  • 5,402
  • 1
  • 19
  • 38
  • I put this problem on the back burner, and today I came back to it. The second part of answer meets all the criteria for what I needed and is generally straightforward to implement. (The standard cryptography class in my language [C#] doesn't allow seeding it's RSA implementation, but that's a language specific complication that I can deal with by using a 3rd party library) – Selali Adobor Aug 19 '14 at 15:03
  • I was worried that I might be misinterpreting aspects of your answer, so I reformatted it and reworded it using my understanding of it. If I changed the meaning of anything please let me know (That would mean that I misunderstood it) – Selali Adobor Aug 19 '14 at 15:12
  • @AssortedTrailmix Looks good. I made a few more small clarifications. – kasperd Aug 19 '14 at 15:42
0

All three of the other answers will allow a user to log in without the server owner being able to see the password. However, they're not really providing any authentication. Authentication is the confirmation that it really is you accessing your own account.

In this scenario, we're assuming that the server admin has full access to the DB (and that'll likely be the case in reality). This means the admin could change the users password to anything he likes, and then log in as that user (and message all his friends or read his private chats or whatever).

I think the only way around this is to have it controlled somewhere centralised. Either implement your own, or use already implemented solutions. This could also give you the added benefit of a single account working across multiple servers, if you care to implement it that way.

Chris Murray
  • 1,275
  • 11
  • 17
  • A malicious server owner could simply modify their build of the server, and have it log everything that's ever done, or disable authentication all together (in general custom builds are encouraged). But nothing useful could be gained from this. Users are notified that "private" messages can be logged for administration (they aren't called private for that reason). The most I aim to do is to limit what the owner can do, only to things within the domain of a single server. So they should be all-knowing/all-powerful within their server, but they can't get passwords, or or log into other servers. – Selali Adobor Aug 19 '14 at 23:04
  • And having an external dependency for multiple servers is something I'm trying to stay away from. Each server is in it's own "domain". Redundancy is implemented within that domain, rather than at a "global" level. The end result is that one user can run a single instance on their LAN for local users with no external connections (another can setup multiple nodes to serve as many users as they wish, and server logins with be shared within their nodes). When looking at the design for this application, you might want to think of a sort "dedicated server" many multiplayer games offer users. – Selali Adobor Aug 19 '14 at 23:10
  • I forgot to ask, how does the accepted solution not authenticate the user? Wouldn't the hash of stored public key being compared in the last step be enough? (My understanding is to "spoof" a user, the client would have to have the same public key and private key [so the same input to the PRNG, and thus the same password]) – Selali Adobor Aug 19 '14 at 23:13
  • @AssortedTrailmix, for multiple servers, fair enough, it was just an idea. Seems you have a good justification against it. As for authentication, the admin wouldn't need the same private key, as he can modify the DB table and change the hash to the hash of his own private key. He can then log in using his own private key to someone else's account. So you can't guarantee that the user logged in is the user it reports to be. Either the admin, or anyone working with the admin could spoof any and every user. – Chris Murray Aug 20 '14 at 07:40
  • That's within the domain of power I want the admin to have. If the admin wants to log in as players on their own server, their only affecting the players of their own server. The server is open source, so the admin wouldn't even have to do anything to the DB. They could allow multiple accounts with the same name, allow any account to be logged into with a "backdoor", etc., and there isn't anything I can reasonably do to stop that. – Selali Adobor Aug 20 '14 at 08:00
  • @AssortedTrailmix, similarly, the admin could change your password hashing algorithm to just save plain text and learn everyone's password. It seems to me that the two ideas (keeping passwords secret from the admin while allowing the admin to do anything he likes) are in conflict. I don't think you can keep the passwords secret while allowing an admin full access to everything. I think you'll just have to weigh up which is more important to you and the project. – Chris Murray Aug 20 '14 at 08:05
  • The accepted answer never shares the password or private key – Selali Adobor Aug 20 '14 at 08:12
  • @AssortedTrailmix, agreed, if the server implements that algorithm. If instead it simply asks for a password, then the admin will gain the password. Basically, you can't do anything that the server admin can't reverse. – Chris Murray Aug 20 '14 at 08:20
  • The server can't just "ask" for any value and get it (otherwise all of this would be moot). The client has to willingly send it, and there's no reason it should. If a malicious party modifies the client then all of this is moot since the client can do whatever it wants with the password (like email it somewhere). I'll be sharing an offical client and the source so people can build it, but a 3rd party sharing a "fake" version isn't something an open-source program has to deal with past ensuring it itself is providing clean builds and a clean codebase. – Selali Adobor Aug 20 '14 at 08:33