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

RSP87
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
nseaSeb
Hello, I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
kpanic
Hi everyone, I am toying with the idea of building a “match maker” for giving personal help to people that wants to start coding. I sta...
New
brecabral
Documentation While reading the Scoped Routes section, I noticed that the documentation currently refers to a problem without explainin...
New
velrest
So my question is quite simple and i have found no conclusive answer on forum, google or AI. Should we use :erlang.float for Integer to ...
New
asweet-confluent
I recently noticed that Elixir’s Logger defaults its primary log level to :debug when no :logger, :level application configuration is pre...
New
apz
I’m new to elixir and just tried to install the elixirLS extension for VScode(ium) and it is throwing some errors that I would like help ...
New

Other Trending Topics Top

GenericJam
Edit: 2026 May 15 - This post is archived. Mob is alive!! Main docs: mob v0.7.11 — Documentation A bit of explanation for the slightly c...
New
JesseHerrick
Hey, I’m Jesse and I’m the main contributor behind Dexter, a full-featured, lightning-fast Elixir LSP optimized for large codebases. It s...
New
mudasobwa
I am happy to introduce the very α version of the new programming language compiled to BEAM. Welcome Cure. It has literally three kille...
New
marciok
Hi there! We created Gust: A task orchestrator inspired by Airflow. For those who have never heard about Aiflow, it’s a Python-based wor...
New
mhanberg
Hi everyone! The first release candidate for the Expert language server project is now available! We’ve published a press release detai...
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

We're in Beta

About us Mission Statement

Options

Thread Display Mode




Thread Preview

Skip Thread Previews