Basic socket.io authentication - node.js

I have the following code to allow users to join a room to start chatting.
server.on('connect', function(data) {
nickname = prompt('What is your name?');
server.emit('join', {name : nickname, room : $('#roomid').val()});
$('#events').append('<li>Welcome, ' + nickname + '!</li>');
});
However, one concern I had is: what's preventing users from emitting 'join' with hundreds of usernames, spamming the chat room? I'm brand new to real-time programming, so I'm wondering what sort of techniques I can use to preventing this sort of behavior.

There's not really anything from stopping a user from doing this by default. You'll want to build in server-side security to handle this kind of thing; a good example is IRC, where some servers have systems set up which limits or disconnects users who get too spammy. Consider these options for additional logic on the server side:
Limit a user (i.e. make it where the events they send are ignored) if they emit more than a certain number of events in a period of time; disconnect/blacklist them if they do it often or particularly excessively.
Ignore events that are emitted in an unallowed state; for example, keep track of the connected socket's username somewhere (i.e. using socket.set), and if they send another join event, discard it.

Related

Socket.io - Is the "socket ID" considered sensitive information?

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.

Practical or Best socket.io implementation

i have a problem over implementing sockets. Case:
the user has n number of rooms in his list,
user should be able to receive notifications from each of the rooms.
method 1) open a socket for each room user has. in this user has to open multiple sockets for each room
method 2) users opens a single socket with room name = userid,
node maintains a list ('room_user') of each room and users in that room (this can be done on connection).
eg
room_user:{
room1 : {
user1Id, user2Id
}
room2 : {
user1Id, user3Id
}
}
For sending a message the server gets the userid's from the list for a specified room and then emits the message in a loop to all users. In this approach the user has to open only one socket but the server has to emit the same message in a loop
i want to know which method would be better suited
If you consider the underlaying TCP/IP broadcast system, you would probably find that it is better that the user have a single websocket connection and the server loop and send the same message again and again (method 2 in your question).
Allow me to explain:
TCP/IP doesn't support broadcasting. For this reason, sending the same message to multiple connections is actually implemented by looping over the list of connections and sending the same message again and again...
It's true that your code will be moving the loop to a higher level of the application, but it would probably be better than having many connections that would hinder your ability to scale the application.

socket.io room authorisation

I have a use case of socket.io where, within an individual namespace, a client can connect to several rooms. A user needs to authenticate on a per-room basis (because they may not be allowed to access those data streams).
Obviously I can check the authorisation on connection to the namespace using a middleware function and some auth data, but unless those rooms are already in socket.rooms when the connection is initiated, I do not know how to check, when a socket joins a room, whether or not it is authorised and subsequently force it to leave the room if it is not authorised.
Is there a join event or equivalent way of doing this? Like the connection event for a namespace but for a room.
EDIT
Having read through the source for socket.io, it appears that no events are triggered when a socket joins a room, but I might have misunderstood something: on reading the source of socket.io-client, joining rooms isn't inherent in the system, suggesting that this is only something that can be triggered on the server side. In that case, I'm assuming I have to manage the client's joining of rooms myself? If this is true, then I can just have something like:
socket.on('join', function(data) { ... });
so that when a socket wants to listen to a particular data stream, it just emits a "join" event, with some data on which room it wants to join, and I handle the whole thing on the server?
Joining a room can only be done on the server. The client typically sends an application-specific message to the server that indicates to your app that they want to join a specific room and then the server carries out that operation on the user's behalf if the request is valid.
So, all you have to do is route all your code on the server that could join a room through one particular function that can do whatever authentication you want to do. For example, you could simply create a function that was the only way your server code would ever put a socket into a room:
function joinAuth(socket, room) {
// can do anything you want here before actually joining the room
}

How to send real time notification to single user using node.js and PHP

I am trying to integrate real time notifications with Node and socket.io in a Symfony Application. I have read a lot of information about this topic and have a working Node application.
nodeClient.js
var socket = io.connect( 'http://192.168.15.106:8080' );
$('a.sendSmile').click(function(){
socket.emit( 'message', { name: 'something' } );
});
socket.on('message', function(data){
console.log(data.name);
});
The problem now is with the above which is working perfectly I am able to send real time notification to all the users at once. But what's the best way to target a single user?
For example a user can send a smile to another user so only the second user should receive the notification and not all the users.
Should I make multiple listeners for node? or any other method to do this?
You need some way of identifying which socket that connected to your server is the one you want to send data to and then you can send to just that socket. You can keep track of user names when users connect or if you have some auth system, you can keep track of which socket belongs to which authenticated user.
Your server holds a list of connected sockets. Each connected one at a time and triggered a connection event on your server when they connected. Your application needs to create a way of knowing which of those connected sockets you want to send the data to. This is not something you've described anything about how you want that to work so we can't really help more specifically.
You can dispatch a notification to single user if you can discriminate that user. For example you can get a user_id on client connection to your nodejs server (the user_id is send from client, inside message) and save it in a key-value store (like Redis, memcache, ...). In this way you can correctly dispatch the notification, arrived from the server (like Symfony2 application), to right user.
I suggest you use Redis, both as a key-value store and for its implementation pattern of the publish/subscribe usable as a channel of communication between the server and the application of realtime.

Can you rely on socket.id as a UID

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.

Resources