ASCrookes

ASCrookes

Hello,

I have recently been working with Phoenix Channels. The whole process has been incredibly straightforward so far! The one thing I have not been able to find has been the security implications when using Channels/Sockets.

I would imagine in a production app with authentication one would want to limit both the number of sockets a single user can have open at once and the number of channels connected.

Currently we limit the number of sessions already so we could tie each user socket’s id to the access token and force disconnect when a session is closed, but that still wouldn’t stop a user from opening multiple sockets per active session.

If anyone can point me in the right direction or if my thinking is wrong about security here let me know!

Showing Posts 1 to 10

wmnnd

wmnnd

I think there are two pretty straightforward ways for doing this:

1: Use Phoenix.Presence. This will allow you to automatically track how often a specific user is currently connected and limit those numbers accordingly.
2: Store a per-user counter in your database or create your own OTP application for holding this kind of state (e. g. using GenServer).

ASCrookes

ASCrookes OP

Ok #1 makes sense as an approach to limiting the channels because I can manually pass a topic and the channel pid to Presence.track/4
Can Presence be used to track at the socket level since it requires the PID of a channel and a topic?

Mazyod

Mazyod

Great question, and I’ve recently when through the exercise myself.

First, you have to understand that blocking multiple sockets at the user_socket level is nasty, since all you can do is return :error. The client socket then gets terminated, and has no clue if it is was an authorization error or multiple sockets error, or even perhaps some other logic you have to block the connection (in my case, client update required!).

With that being said, you might not care about returning the exact error to the user, or maybe you do:

  1. If you do: You need to allow the user to connect to a socket, and then make it obligatory that all active users connect to some global channel (in my case it is global:[USER_ID]). Once you accept the socket, and the user attempts to join this global channel, it becomes trivial to detect multiple sockets using Presence, :global.where_is, or Elixir v1.4 pid Registry.
    I personally tried presence, but it ends up adding extra baggage like pushing presence state down to the client, which I don’t want. I was using :global.where_is because I am still stuck at Elixir v1.3, but it worked fine.

  2. If you don’t: Just use :global.where_is or Registry to register the user socket under their user id, then check if a process exists when another socket connection comes in.


I’ll tell you this though, at the end, I scrapped it. The reason I wanted it in the first place was because a user would end up in a game room with themselves (given they connect on two different sockets). The best solution I found and implemented was to add the checks in the room channel and game channel level not at the socket level. If the user wants to connect on multiple devices, sure why not! The real problem is elsewhere and should be solved on a different level.

chrismccord

chrismccord

Creator of Phoenix

You can intercept presence_diff in your channel and drop the message:

intercept ["presence_diff"]

def handle_out("presence_diff", _, socket), do: {:noreply, socket}
OvermindDL1

OvermindDL1

I use this extensively, now if only you could tell it to use a different message than presence_diff though, I tend to have more than one presence in a channel. ^.^

chrismccord

chrismccord

Creator of Phoenix

You can rewrite the topic yourself fwiw by intercepting, which is probably exactly what you’re doing :slight_smile: It’s not something I’m likely to support in Presence because we would have to track the event per subscriber which would add complexity.

OvermindDL1

OvermindDL1

It is precisely. :wink:

mgwidmann

mgwidmann

I built an expiration mechanism for our sockets so that it made sure the user could not stay connected longer than their JWT token was valid for. I thought I’d share my experience since it may be of some use to you.

In order to do that, I had to create a GenServer which would take registrations of new sockets when they’re opened and their associated token and keep them in the state of the gen server. Periodically, the gen server would go through and look for expired tokens and if it found one and the user was still connected (i.e. the socket pid was still alive) I broadcasted a “user-1234:disconnect” message which the generated socket code shows you how to do. This disconnects all users, and the client should then attempt to reconnect. Since I denied the connection when the token is no longer valid they would not get valid connection and the after error callback would fire on the client. The client then would request a new token and reattempt connection (only once).

You may be able to do something similar, just in your case when you limit the reconnect attempts to one or more tries, you’ll have to assume then that the reason is because of too many sockets and tell the client to give up trying by doing a socket.disconnect().

ASCrookes

ASCrookes OP

Ok this also sounds like a very solid approach on this.
Thanks for being so descriptive, will definitely factor this into our end solution.

ASCrookes

ASCrookes OP

Hm thats a great insight re: blocking at the socket level.
For now I’m going to add limits to the number of channels that can be joined.

Thanks for the feedback!

— All posts loaded —

Where Next? Top

Trending in Questions Top

stjefim
Hello! Suppose you are building workflow (order / task / payment) processing system with the following requirements: Each workflow con...
New
water
I’m aware of 2 such projects, are there others? git: GitHub - team-alembic/realworld: A fullstack Phoenix LiveView application with back...
New
itzmidinesh
Hi all, I’m working on a Phoenix project where I need to make external HTTP requests and I’m trying to choose between Req and HTTPoison....
New
peck
Hi everyone. My team and I have been working on a fairly modest app based around video streaming and chat, but we’ve landed a customer t...
New
leoschweizer
Hi, we run different LLM models across a cluster, with each node loading a configurable subset. Model-specific queues let nodes consume ...
New
csmr
Hi all, I’m new here and would like to hear peoples views on my Elixir/Phoenix based game-engine! :smiley: Mutonex gamedesign was finish...
New
Stef-Verniers
Hello I’ve been coding in Elixir for almost two years now because of my current job where we implemented Elixir in our application. But ...
New

Other Trending Topics Top

dominicletz
Hey there, I haven’t seen a list of useful Elixir specific skills for the popular AI tools yet so I’m starting one here with my first. ...
New
mudasobwa
I fully migrated to my own harness from Anthropic/Gemini and I think it’s time to share it. Welcome DSH, the DeepSeek Harness, fully writ...
New
KristerV
Hey. Is there anyone here who creates agents in their apps? Not talking about using agents, but creating them. I’m finding it pretty diff...
New
jimsynz
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
webofbits
With AI doing more of the implementation work, I’ve been wondering how much coding I should deliberately keep doing myself. My main conc...
#ai
New
OndrejValenta
(I just needed to vent somewhere and LinkedIn is full of hope, or hype, I’m not sure which exactly) AI dream has many faces, but general...
New

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews