Excuse my ignorance, day 2 of node.js/socket.io
I'm looking for a way to uniquely identify users for use in a database queuing system. I read a lot about using Express's session cookie, however I've noticed socket.id seems to be an UID that socket.io is already using.
Therefore I have been using socket.id to identify my users both in the database, and in creating private "rooms" to communicate with just them.
Is this a terrible idea?
A socket ID is just that - it uniquely identifies a socket. It doesn't uniquely identify a user, and it's definitely not intended to be used for that purpose. A single user (in many applications) might have multiple connections (and therefore multiple sockets with different ID's). Also, every time they connect they will be assigned a new ID.
So you obviously shouldn't use a socket.id as a user ID. Mustafa points out that you could reassign socket.id to a user ID, but I tend to think that's a very bad idea for two reasons:
socket.id is supposed to uniquely identify a socket, so you would run into problems when a single user has multiple sockets open.
Socket.IO, uses that ID internally a lot for storing things in hashtables, and if you change the ID, you might get unexpected results and hard to track down bugs. I haven't tested it, but looking at the Socket.IO source, that's what I would expect.
Better to generate ID's using another method then associate a user with a socket (for example, during the handshake using data from the cookie).
socket.set(key, value, callback) is the method explicitly intended to be used for associating your own data (like a user ID) with a socket connection, and is the only one guaranteed to be safe.
When the socket.io sockets are created, you can add variables as you wish to socket object. socket.userid = getUserID() will work fine. It is better to make assinging UIDs in database, and add them to socket objects when their authentication is succesful.
Related
I am working on setting up socket.io in cluster mode using PM2.
I am using socket.io-redis package and it works fine in cluster mode.
But the problem arises when I want to access all connected sockets. Because processes don't know about socket connections in other processes in cluster mode.
I thought socket.io-redis keeps track of all the connected sockets and all its session info but it didn't.
Is there any way or solution to access all the socket connection existing in all processes in socket.io/Nodejs?
Socket.io-redis does keep track in a sense..
From their docs
"The Redis adapter extends the broadcast function of the in-memory adapter: the packet is also published to a Redis channel (see below for the format of the channel name).
Each Socket.IO server receives this packet and broadcasts it to its own list of connected sockets."
So basically, redis is used as the broker to tell each socket server to emit based on X channel etc. Allowing you to have a socket.io server in cluster mode work, but as you have mentioned it can fall short when you need to keep track of things outside of just an emit.
So where does this leave us.. Well you can use custom hooks via socket.io-redis but personally I found it to be really difficult to understand and use and had limited success personally. I think with the new version of socket.io and socket.io redis there were some tweaks to make this simpler however I have not tried them.
Instead, what we do is use redis hset and jget to store the socket and an ID of a users, then when we want to get all users online we can query redis to get the list of online users or users in a specific room etc.
What you will want to do is add the redis package and connect in additon to the regular pub / sub.
Then, when a user joins a room or your server for that matter you will do an hset. On the first join ours looks something like this
redis.hset([collection-name],[Field],[value])
So in code it looks like
redis.hset(decoded.cID,"socket-" + socket.id,socket.nickname)
This will set a value in redis, so the collection name is a value ( for us its a unique id of the channel ) then we stock the 'socket.id' for the Field along with a 'nick-name' for the value. This value is the users ID OR its anonymous if they are not logged in
Then, when we want to grab who is in a room we use the hget command
redis.HGETALL([collection-name],function(err,results){}
So inside of say the emit, we call the redis.HGETALL command to get all items inside a specific collection that we pass in and send that back to all connected users.
I'm realizing a simple chat application with Node.js and socket.io and wonder if sharing the socket IDs of all the clients throughout Objects is considered a good practice?
To explain, each of my users are represented so :
{
nick : 'John Doe',
dateJoined : Date.now(),
rank : 0,
id : socket.id
}
A list of all my clients is stored on the server const usersList = [ {...}, {...}, ... ]
All of my clients needs to see who's connected, so this usersList should be shared to all clients.
And if a client disconnect, all should be notified that this #ID disconnected :
socket.on('userDisconnect', function(id) {
clientSideList = clientSideList.filter( user => user.id !== id );
}
So my question is : is that a security problem if every client knows the socket ID of the others? Could they make something nasty like stealing data or impersonating other users?
Thanks for your help
There is no inherent, built-in security risk in sharing the socket.id value of one or more sockets. socket.io itself does not contain any APIs that a client could use to use that socket.id to do anything. So, out of the box, no client could do anything with a socket.id if they knew it.
Now, if you start supporting socket.io messages that accept socket.id values as arguments, then passing around socket.id values does allow some arbitrary client to use those socket.id values in your messages. Whether or not that causes a problem depends entirely upon your own design and what messages your server accepts. Suppose you support a message that tells the server to remove a user from your system by just passing it the socket.id of that user. Then, if you start passing around socket.id values, then that allows anyone to use your own server message to remove that user from your system.
So, there's no built-in risk with socket.id values. There could be risk if your own server supports operations that can do damage when given a socket.id value. So, that's totally up to how you've designed your server and whether you've protected against malicious operations when someone knows a socket.id.
You can think of a socket.id like a temporary username on the socket.io server. In most systems, knowing only the username of some user does not, by itself, cause a security problem. It's only if you expose operations that an unauthorized client can then direct at a specific username that you get a problem. Same for socket.id. It's just like a temporary username.
When a new connection is handled by Socket.IO, it creates a Socket object, and one of this object's properties is id. This is an "unguessable" (according to the documentation) string used to identify the connection.
When building applications, is it considered safe, secure and good practice to broadcast this ID to other clients? My inclination is that hashing the ID and broadcasting the digest may be a better idea.
The id is not sensitive data unless your own code somehow makes it sensitive. There are no socket.io client operations that take the socket id, so if a client has a socket id, there is nothing they can do with it unless your code implements message that operate on it.
It is unguessable so that you can use it as an id to share with other clients while not allowing that client to know who's id they might have been given (like an anonymous id).
So, using it as an identifier to refer to some other user's socket is exactly what it was intended for and does not cause a security or privacy issue unless your own client operations cause such.
I'm trying to make a push notification system, but to do that I need to find the proper way to emit something to ALL active sockets connections of a user (we suppose the logged-in User1 has opened my website in more than one tab of his web browser)
So, I was saving all active sockets-id in MySQL when a user was connected to his account (with sessions) :
user_id | socket_id
1 | 4lf3dsg2bx1u9d # User 1...
1 | 75ksdfzfff1u3d # ...has two active socket connections
2 | GEdsfoorbar1z4
and something like the following in my script :
io.sockets.sockets[socket_id].emit(...) # for each sockets of User1
But, I'm sure MySQL isn't the best way to sort sockets...
Does someone have a better solution ? Like a io.sockets.has('user=1') or something magic ?
I also tested the solution to stock every sockets by user-id in an Object but what about performance ?
Your strategy of storing socket IDs isn't horrible, although storing them using something like Redis would be better than MySQL. But for your case, you don't need to store the socket IDs at all: just use Socket.io's rooms instead. For example, every time User #1 establishes a socket connection, on the server do:
socket.join('user_1');
Then broadcast to everyone in the room (i.e. all the sockets that the user is connected with) using:
io.sockets.in('user_1').emit('event_name', data);
You can actually retrieve the IDs of all the sockets in a room, but the only time I really find it necessary is to count the number of IDs to see if the user is still connected.
Full docs for rooms are on the Socket.io wiki.
In our app, every time a user signs in a new connection in socket.io is created. So if a user signs in simultaneously on more devices, they behave as separate connections. Instead of creating a new connection every time, I'd like to check whether the user is already connected to socket.io and if he is, I'd like to connect him to already established connection. How can this be done?
From the socket.io perspective, those ARE two connections. But if it was my app, I would do something like this:
add a user identifier (userid, username, something you identify users by) to both the users' socket.io connection (so, each time you send a message to the client, you also pass this id)
pass this id also to the client-side code on init.
So now the socket on the server side has its' socket.io id, but also your user id. Anyway, to proceed:
send this user-id to the client in it's javascript files.
on the client-side code, make a small adjustment to socket.io handler - for each received message (say, broadcast), you can now check if it's your current user-id and instead of saying 'User #351 says Hi' you can say 'You said Hi on another device' or something like that.
Of course, I'm not socket.io expert, there's could already exist a framework or lib addressing this.