LOC-2: Design Authentication Mechanism



Issue Information

Issue Type: Task
 
Priority: Major
Status: Closed

Reported By:
Ben Tasker
Assigned To:
Ben Tasker
Project: LocalChat (LOC)
Resolution: Done (2018-05-13 12:56:43)
Target version: V0.0.2,
Components: Authentication ,

Created: 2018-05-12 12:06:53
Time Spent Working


Description
Need to start thinking more seriously about how best to handle authentication (any by extension, encryption).

My original plan had been that the "room password" would never be shared with the server and instead would be used as a shared encryption key by the clients to encrypt message payloads. Despite that, for some reason, I added a passhash field to the rooms table.

We also need some level of user auth when they join a room. Knowing the room password shouldn't be sufficient to let them retrieve messages (as they'd then be able to decrypt them).

One option might be to merge the two into one, of the format roompass:userpass and just have the client split them and use each appropriately.

In any case, needs some thoughts laid down on the best approach so that decisions are recorded.


Issue Links

Toggle State Changes

Activity


As of LOC-4 the room password is now used as a shared key for the E2E encryption. So we definitely shouldn't have that sent to the server when creating a room (otherwise it'll be sat on the information - even if hashed - needed to decrypt user's messages).

So passhash should probably come out of both the rooms table and the relevant API calls.

We do still need to design and build the user authentication though. Currently, if you know the room owner's username and the room password you can sign in as them.

There's a related tangent here though too. LOC-5 addressed a scenario where users can pretend to be the SYSTEM user and can push what appear (to other users) to be system messages. The changes in that issue stop users from signing in as SYSTEM and pushes warnings if they try to do any of the setup steps required to achieve that. However, the sending user's name is currently stored (a second time) within the encrypted message payload - it's that value which is used by the client.

So it's still possible for a malicious user to send messages that'll appear to be from SYSTEM by making a call that uses their username in the server visible field but uses SYSTEM in the encrypted version.

The reason I haven't fixed that yet is because we first need to work out the best means of authenticating the sending user's requests. Ideally the message table shouldn't contain a plaintext list of usernames as it becomes too easy to identify patterns - which user sends messages most frequently, etc. Fixing that has a massive overlap with the auth mechanism.

So, there are a few challenges to be overcome here:

- Need to generate (preferably client side) a password for new users and store a hash for them on the server
- A joinRoom call needs to authenticate their request
- Assuming authentication succeeds, need to set something so that other requests can also be validated
- Ideally, that something can then be used to help close the remaining sending as SYSTEM hole, whilst allowing us to remove cleartext user names from the messages table



Repo: LocalChat
Host:Rimmer

commit 5211d7f7de1f279082f65f29cf935d74501e1b9d
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 09:26:51 2018 +0100

Commit Message: LOC-2 - Removed Room password from API and from table

It's used as a crypto key, so we don't really want any aspect of it going anywhere near the server.

server/LocalChat.py | 9 ++++-----
1 files changed, 4 insertions(+), 5 deletions(-)


View Commit | View Changes
Repo: LocalChat
Host:Rimmer

commit a23124d5b52f5863f8f61647f0b4f34759c037fc
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 09:27:40 2018 +0100

Commit Message: LOC-2 Removed room password from submission and from input format of /room create

New syntax is

/room create [roomname] [owner]

The client will then generate client side a password to use as the room password. It's perfectly fine to use a different value, so long as all other users in the room are using the same one.

What will need to happen when creating a room, though, is for us to submit a password hash for the owning user so that they can actually enter the room to invite others.

client/LocalChatClient.py | 31 +++++++++++++++++++------------
1 files changed, 19 insertions(+), 12 deletions(-)


View Commit | View Changes
Repo: LocalChat
Host:Rimmer

commit b27c039a069fa2112c0eba7f743f80b8fbe70480
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 10:41:18 2018 +0100

Commit Message: LOC-2 Implemented user password storage

* The backend now stores a (bcrypt) hash of a user's password.
* When a room is created, a hash of the owner's password is also inserted into the invite table so that they can join the room

Currently, the password isn't actually needed to join (that's the next step)

Within the client, when a room is created output is generated to show how to join the room. This commit amends that output to include the admin user's password, for example

To join the room, do /join BenTest123 SD8WXY1OKC39CI0Y:QCVN7CEPUCHAQU2S ben

The first section (i.e. before : ) of that password is the room password (so will be used for the E2E encryption). The second half is the users password.

When invites are generated (this doesn't yet happen) the generated output will differ only in the second half of the password and the username to sign in as.

client/LocalChatClient.py | 31 ++++++++++++++++++++-----------
server/LocalChat.py | 27 ++++++++++++++++++++-------
2 files changed, 40 insertions(+), 18 deletions(-)


View Commit | View Changes
Repo: LocalChat
Host:Rimmer

commit 8f3cda6c30d143e79224aeec29c67aa7bc14874e
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 10:50:19 2018 +0100

Commit Message: LOC-2 Implemented password generation when inviting users

Now, when a user is invited into a room the inviting user will be given output detailing how the invited user should join

User ben2 may now join room
To join the room, they should do /join BenTest7 0JF6LXE4Q23TFE7A:U2SNQORSY9NZWV7T ben2

That will obviously need to be communicated to the invited user securely, but there's already a need to securely communicate how to connect to the LocalChat instance anway, so it's effectively an extension of an existing need to do a key-exchange.

client/LocalChatClient.py | 10 ++++++++--
server/LocalChat.py | 2 +-
2 files changed, 9 insertions(+), 3 deletions(-)


View Commit | View Changes
Repo: LocalChat
Host:Rimmer

commit 4eac571d338fa478190e80dd48e443b0981d236f
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 11:03:36 2018 +0100

Commit Message: LOC-2 Enforce user password when joining room

When joinRoom is called, we'll now check that the submitted password matches the hash in the database.

That's currently as far as the authentication mechanism goes at the moment though, so once a user's entered a room, other users could still submit messages with their name on it. Addressing that is up next.

client/LocalChatClient.py | 2 +-
server/LocalChat.py | 90 +++++++++++++++++++++++++--------------------
2 files changed, 51 insertions(+), 41 deletions(-)


View Commit | View Changes
So the next thing to decide, is what mechanism to use to validate that a request purporting to be from a user is actually by that user.

The simplest would seem to be some sort of a session key, just need to make sure it's sufficiently random as not to be easily guessable.
Repo: LocalChat
Host:Rimmer

commit 462e9dfd34d7e338abceea1fd896718a5c68b12b
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 11:23:07 2018 +0100

Commit Message: LOC-2 joinRoom now generates and returns a 1024 bit session key

When the user successfully joins a room, a 1024 bit session key is generated and returned. The client stores this for use with later requests.

It's use isn't currently enforced (coming soon) and the table isn't currently tidied when a user leaves/is kicked (again coming soon).

The session key is prefixed by the room id, in the format

[roomid]-[sesskey]

The idea being that when a room is closed, it's trivial to remove all related sessions.

My concern here, though, is that it may leak how many rooms there are. If your session key starts 10 then you know there are (or have been) 10 rooms (including yours).

So, either we need to change the prefix to be the room name, or perhaps just have the server seed the room IDs with a random number at startup.

The problem with the latter is that we risk running out of IDs if the number's truly unbounded, and if it is bounded and we're near the upper bound you can still take an educated guess.

So it's probably better to switch using the room name in hindsight. Will change that next

client/LocalChatClient.py | 2 ++
server/LocalChat.py | 20 ++++++++++++++++++--
2 files changed, 20 insertions(+), 2 deletions(-)


View Commit | View Changes
Repo: LocalChat
Host:Rimmer

commit cca924ffe5698ab99baa6eb2e87d5310534756bc
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 11:30:11 2018 +0100

Commit Message: LOC-2 Switch to prefixing with Room Name so that we don't leak how many active rooms there are

server/LocalChat.py | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)


View Commit | View Changes
Repo: LocalChat
Host:Rimmer

commit 4b7ea09ba04b88c1df6564ae3124f25db1b2966d
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 11:34:48 2018 +0100

Commit Message: LOC-2 Client now sends session key with all relevant requests

Having joined a room, the client will now include sesskey in it's payload for

- pollMsg
- sendMsg
- leaveRoom
- closeRoom
- inviteUser
- kickUser
- banUser

And will clear the stored key when a user leaves the room.

client/LocalChatClient.py | 17 ++++++++++++-----
1 files changed, 12 insertions(+), 5 deletions(-)


View Commit | View Changes
Repo: LocalChat
Host:Rimmer

commit ad8e92222f3ae530e01b9dc4b625039db995bf33
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 11:42:52 2018 +0100

Commit Message: LOC-2 Session token is now checked and validity enforced

server/LocalChat.py | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)


View Commit | View Changes
Repo: LocalChat
Host:Rimmer

commit f5a468cab1e72ed94ce53d02a2f2f9f83de570ea
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 11:47:24 2018 +0100

Commit Message: LOC-2 - Delete user's session token when they're kicked/banned or leave the room

server/LocalChat.py | 9 +++++++++
1 files changed, 9 insertions(+), 0 deletions(-)


View Commit | View Changes
Repo: LocalChat
Host:Rimmer

commit 2c0106df1c185ca2add6e2b1d9261e0cbdd9cb54
Author: B Tasker <github@<Domain Hidden>>
Date: Sun May 13 11:51:05 2018 +0100

Commit Message: LOC-2 Clear user sessions when room is closed

server/LocalChat.py | 2 ++
1 files changed, 2 insertions(+), 0 deletions(-)


View Commit | View Changes
OK, we now have an authentication mechanism in place, and it should be reasonably robust. Which unblocks us from thinking about how to deal with the known sender spoofing issue, though I'll raise a separate issue for it.
btasker changed status from 'Open' to 'Resolved'
btasker added 'Done' to resolution
btasker changed status from 'Resolved' to 'Closed'