I'm making a webpage based around players being able to invite other players to parties, and other things a long the lines.
I have your basic send / receive / update of the chat/users in your party. The only thing is, what's to stop somebody from sitting there opening up a developer console and going
socket.emit('updateUsers', 'Weiner');
socket.emit('updateUsers', 'Idiot');
socket.emit('updateUsers', 'Bad word');
socket.emit('updateUsers', 'Other stupid malicious really long spam the chat name');
How can I prevent against this so that they can not do such things?
(Full JS Stack, Node.js)
Thanks!
I faced this problem aswell, This was my solution as far as spamming emits go (malicious socket use)..
var spamData = new Object();
var spamCheckFunctions = ["updateUsers","moreEmits"]; // anti-spam will check these socket emits
var antiSpam = 3000; // anti spam check per milliseconds
var antiSpamRemove = 3; // -spam points per antiSpam check
var maxSpam = 9; // Max spam points before disconnect is thrown to the socket
io.sockets.on('connection', function (socket) {
// Spam Check, this binds to all emits
var emit = socket.emit;
socket.emit = function() {
data = Array.prototype.slice.call(arguments);
if(spamCheckFunctions.contains(data[0])){
addSpam(socket);
};
emit.apply(socket, arguments);
};
var $emit = socket.$emit;
socket.$emit = function() {
data = Array.prototype.slice.call(arguments);
if(spamCheckFunctions.contains(data[0])){
addSpam(socket);
}
$emit.apply(socket, arguments);
};
});
function maxSpamCheck(socket){
if(spamData[socket.username].spamScore>=maxSpam && !socket.spamViolated){
socket.spamViolated = true;
socket.disconnect();
}
}
function checkSpam(){
for(user in spamData){
if(spamData[user].spamScore>=1) spamData[user].spamScore-=antiSpamRemove;
}
return;
}
setInterval(checkSpam,antiSpam);
function addSpam(socket){
if(socket.spamViolated) return;
spamData[socket.username].spamScore+=1;
maxSpamCheck(socket);
}
// Then add this where your user is authenticated
function authenticate(socket){
socket.username = username // here you define username
socket.spamViolated = false;
spamData[socket.username] = {
spamScore: 0
}
}
Array.prototype.contains = function(k) {
for(var p in this)
if(this[p] === k)
return true;
return false;
};
basically binds to all emits and checks if the emit name is contained in spamCheckFunctions if it is it will add a spam point, if a user exceeds a spam score amount (maxSpam); he will be disconnected. And for every milliseconds defined at antiSpam will minus the user spam score defined at antiSpamRemove
I'm sure there are cleaner solutions but this one worked out pretty good for me :)
Just make sure to verify/authenticate the users.
this is how I authenticate them (not using nodejs as a webserver, but had django):
io.configure(function(){
io.set('authorization', function(data, accept){
if(data.headers.cookie){
data.cookie = cookie_reader.parse(data.headers.cookie);
return accept(null, true);
}
return accept('error', false);
});
});
now you can access socket.handshake.cookie['sessionid'] (in my case this worked with django)
then match the socket.handshake.cookie['sessionid'] with a entry where your sessions are stored on the webserver
That's a difficult problem in general. Two things you could do:
1) Use self-invoking functions on the client side, i.e.
(function(w) {
// define your sockets here
var socket = ...;
})(window);
Obviously it is on the client side so this is not really secure. But it's not bad to have such wall.
2) On the server side keep track of the frequency of posting. For example if someone posts 5 times in a second, then you can assume that it is a spam and you could block that socket. It is especially effective if combined with authentication and complex registration (so people will have problem in creating new account).
Use an md5/sha key which behaves like a cookie.
Generate a key for a specific user and send it to the client and always check that incoming requests have the same key
It won't be completely secure since the hacker can always find your key in either the source code or localStorage but try to hide it through obfuscation of your code
A way to prevent spam is to either implement user authentication and/or a packet rate limiter. Add a middleware function which keeps track of the socketId and the amount of packets being sent through that socket. When it exceeds your limit disconnect the socket.
You can even add an extra function which keeps track of the IP address of that socket, if an IP address will be disconnected too often due spam you can ban that ip. Add a check on your connection event which IP addresses are allowed.
Use rate-limiter-flexible package for limiting number of events per second. Limiting by IP is the most simple, but would be better to limit by userId if possible.
const app = require('http').createServer();
const io = require('socket.io')(app);
const { RateLimiterMemory } = require('rate-limiter-flexible');
app.listen(3000);
const rateLimiter = new RateLimiterMemory(
{
points: 5, // 5 points
duration: 1, // per second
});
io.on('connection', (socket) => {
socket.on('bcast', async (data) => {
try {
await rateLimiter.consume(socket.handshake.address); // consume 1 point per event from IP
socket.emit('news', { 'data': data });
socket.broadcast.emit('news', { 'data': data });
} catch(rejRes) {
// no available points to consume
// emit error or warning message
socket.emit('blocked', { 'retry-ms': rejRes.msBeforeNext });
}
});
});
Read more in official docs
Related
So i noticed that you can run 'io()' in console on client side.
I'm worried that if someone were to loop it, it would crash the node.js server.
Does anybody know how to prevent multiple connection for the same user.
It is a fairly complicated process to do that properly.
But on that same note, people won't be able to crash your server with socket.io as easy as you think they would be able to.
Node.js can handle a ton of connections at once, same with socket.io. Obviously these are both dependent on what your server actually is; but even as Raspberry Pi can handle a significant amount of connections.
But, if you truly must implement this, I'd recommend checking out this issue and just making a counter-based dictionary of IP's and to disconnect sockets if their IP goes above a specific number.
Get the client's IP address in socket.io
Very crude, but it would do what you need.
you need some helper function on server side
get user ip with this package:
npm install request-ip
create array of users:
let users = [ ];
validate and add user to array on new join request
const requestIp = require('request-ip');
const addUser = () => {
const ipMiddleware = function(req, res) {
const clientIp = requestIp.getClientIp(req);
};
const existingUser = users.find(user.clientIp === clientIp)
if (existingUser) {
return false
}
const newUser = { clientIp };
users.push(newUser)
return true
}
I'm learning about node.js, express, and socket.io
I've managed to successfully set up my server and connect to it from the browser using localhost:3000
I can send information from client to server and vice versa no problems.
What I now need to do is this.
Let's say we have 2 clients connected to the server.
Each client will send a piece of information.
socket.emit('mydata', datavariable);
On server:
socket.on('mydata', function(datavariable) {
myArray.push(datavariable); //myArray declared globally
});
How do I code it so the server will do something like this.
if(myArray.length = #ofconnectedclients) {
socket.emit("filledUpArray", myArray);
}
Or another way of saying it I guess is, once you have gotten a response from all clients connected, then do something.
I want to receive a piece of information from all clients, and after I have received that info from all clients, then send that data (now stored in an array) to all my clients.
Thanks!
So, it sounds like your problem statement is this:
When all connected clients have sent a certain piece of information to the server, then I want the server to do something.
First thing you have to do is keep track of all connected clients. You can either use socket.io's internal data structures for that or you can create your own. Here's creating your own on the server:
// map of all currently connected client objects
// socket is the key, custom object is the value
// methods are for managing data in the Map
let connectedSockets = new SocketMap();
// socket connects
io.on('connection', function(socket) {
let socketData = {};
socketData.socket = socket;
socketData.dataReceived = false;
// add it to our Map
connectedSockets.set(socket, socketData);
// monitor disconnects too
socket.on('disconnect', function() {
// remove it from the map
connectedSockets.delete(socket);
});
});
The second thing you need to do is to keep track of some per-socket state so you can initailize it at some point and then each time you receive a specific message from a client, you can check to see if all clients have sent the desired message. We'll create a couple functions to do that:
// this actually physically goes above the previous code, but I'm just placing it
// here for explanation purposes
class SocketMap extends Map {
// clear the dataReceived Boolean to false for all sockets
initReceived() {
for (let [key, value] of this) {
value.dataReceived = false;
}
}
// return Boolean if all sockets have dataReceived set to true
haveAllReceived() {
for (let [key, value] of this) {
if (!value.dataReceived) {
return false;
}
}
return true;
}
setReceived(socket) {
let data = this.get(socket);
if (!data) {
return false;
}
data.dataReceived = true;
}
}
Now you have methods on the connectedSockets Map object for keeping track of a dataReceived Boolean for each socket and for initializing them all to false and for setting one to true when a particular condition occurs (receiving a message) and for checking if they have all set the flag. So, you just have to determine when, in your code you want to call those messages.
Let's suppose that when the message you're waiting for from each socket is someMsg. So, you create a listener for that message and process it. We can incorporate that code into our previous connection handling code here:
// map of all currently connected client objects
// socket is the key, custom object is the value
// methods are for managing data in the Map
let connectedSockets = new SocketMap();
// socket connects
io.on('connection', function(socket) {
let socketData = {};
socketData.socket = socket;
socketData.dataReceived = false;
// add it to our Map
connectedSockets.set(socket, socketData);
// monitor disconnects too
socket.on('disconnect', function() {
// remove it from the map
connectedSockets.delete(socket);
});
// process this message
socket.on('someMsg', function(data) {
connectedSockets.setReceived(socket);
// if all sockets have now sent their data, then do something
if (connectedSockets.haveAllReceived()) {
// do your thing here
// reset state, ready for next time
connectedSockets.initReceived();
}
});
});
I'm having trouble displaying the correct amount of online users. A question similar to this have shown me that I can count the users this way:
var count = 0
socket.on('connection', function(client) {
count++;
client.broadcast({count:count})
client.on('disconnect', function(){
count--;
})
})
The issue I'm constantly running into with that method is that when a user happens to reload the page too quickly, the counter pulls in too much it can throw out.
As you can see, on the right side of the image, a user spammed the reload and it caught more users online than there actually is. (There was only one user on the server at this time)
My question is is there a better or more reliable way to count the exact amount users online without the extra 'virtual users', without using the users++/users-- method?
If they're logging in as a user, then you should authenticate them to the socket. Use that authentication to see if they already have a session, and disconnect them decrementing the count, before you increment it again with the new session.
An example below. The clients objects stores the connected clients, with values being the sockets they're connected to.
var clients = {};
socket.on('connection', function(client) {
//Authenticate the client (Using query string parameters, auth tokens, etc...), and return the userID if the user.
var userId = authenticate(client);
if ( !userId ) {
//Bad authentication, disconnect them
client.disconnect();
return;
}
if (clients[userId]) {
//They already have a session, disconnect
clients[userId].disconnect();
}
//Set session here
clients[userId] = client;
client.broadcast({count: Object.keys(clients).length})
client.on('disconnect', function(){
delete clients[userId];
})
})
Could do this pretty cleanly with the Observable pattern (using RxJS v5 here):
const { Observable } = require('rxjs')
const connections = Observable.fromEvent(socket, 'connection').mapTo(1)
const disconnections = Observable.fromEvent(socket, 'disconnect').mapTo(-1)
// emit 1 for connection, -1 for disconnection
Observable.merge(connections, disconnections)
.scan((total, change) => total + change, 0) // emit total
.subscribe(count => client.broadcast({ count }))
You can use Server Sent Events for this purpose really.
Take a look at https://javascript.info/server-sent-events
I have a node application handling some ZeroMQ events coming from another application utilizing the Node-ZMQ bindings found here: https://github.com/JustinTulloss/zeromq.node
The issue I am running into is one of the operations from an event takes a long time to process and this appears to be blocking any other event from being processed during this time. Although the application is not currently clustered, doing so would only afford a few more threads and doesn't really solve the issue. I am wondering if there is a way of allowing for these async calls to not block other incoming requests while they process, and how I might go about implementing them.
Here is a highly condensed/contrived code example of what I am doing currently:
var zmq = require('zmq');
var zmqResponder = zmq.socket('rep');
var Client = require('node-rest-client').Client;
var client = new Client();
zmqResponder.on('message', function (msg, data) {
var parsed = JSON.parse(msg);
logging.info('ZMQ Request received: ' + parsed.event);
switch (parsed.event) {
case 'create':
//Typically short running process, not an issue
case 'update':
//Long running process this is the issue
serverRequest().then(function(response){
zmqResponder.send(JSON.stringify(response));
});
}
});
function serverRequest(){
var deferred = Q.defer();
client.get(function (data, response) {
if (response.statusCode !== 200) {
deferred.reject(data.data);
} else {
deferred.resolve(data.data);
}
});
return deferred.promise;
}
EDIT** Here's a gist of the code: https://gist.github.com/battlecow/cd0c2233e9f197ec0049
I think, through the comment thread, I've identified your issue. REQ/REP has a strict synchronous message order guarantee... You must receive-send-receive-send-etc. REQ must start with send and REP must start with receive. So, you're only processing one message at a time because the socket types you've chosen enforce that.
If you were using a different, non-event-driven language, you'd likely get an error telling you what you'd done wrong when you tried to send or receive twice in a row, but node lets you do it and just queues the subsequent messages until it's their turn in the message order.
You want to change REQ/REP to DEALER/ROUTER and it'll work the way you expect. You'll have to change your logic slightly for the ROUTER socket to get it to send appropriately, but everything else should work the same.
Rough example code, using the relevant portions of the posted gist:
var zmqResponder = zmq.socket('router');
zmqResponder.on('message', function (msg, data) {
var peer_id = msg[0];
var parsed = JSON.parse(msg[1]);
switch (parsed.event) {
case 'create':
// build parsedResponse, then...
zmqResponder.send([peer_id, JSON.stringify(parsedResponse)]);
break;
}
});
zmqResponder.bind('tcp://*:5668', function (err) {
if (err) {
logging.error(err);
} else {
logging.info("ZMQ awaiting orders on port 5668");
}
});
... you need to grab the peer_id (or whatever you want to call it, in ZMQ nomenclature it's the socket ID of the socket you're sending from, think of it as an "address" of sorts) from the first frame of the message you receive, and then use send it as the first frame of the message you send back.
By the way, I just noticed in your gist you are both connect()-ing and bind()-ing on the same socket (zmq.js lines 52 & 143, respectively). Don't do that. Inferring from other clues, you just want to bind() on this side of the process.
I was trying to learn node and started creating a mashup with socket.io
The message transportation have begin but I have run into some trouble.
The message event is firing multiple times leading to a single message appearing multiple times on the recipient's box. I have routed the socket to exports.chat and was wondering if that is causing the problem?
To narrow down the problem: the messages are firing the number of times = the sequence of connection of the client. That is, if a client connects second, his messages will fire twice. three times for the client connecting third.
Here is the code snippet:
exports.chat = function(io, pseudoArray, req, res){
res.render('chat', {title: 'ChatPanel.'});
var users = 0;
io.sockets.on('connection', function (socket) { // First connection
users += 1;
// reloadUsers(io, users);
socket.on('message', function (data) { // Broadcast the message to all
if(pseudoSet(socket)) {
var transmit = {date : new Date().toISOString(), pseudo : returnPseudo(socket), message : data};
socket.broadcast.emit('message', transmit);
console.log("user "+ transmit['pseudo'] +" said \""+data+"\"");
}
});
socket.set('pseudo', req.session.user, function(){
pseudoArray.push(req.session.user);
socket.emit('pseudoStatus', 'ok');
console.log("user " + req.session.user + " connected");
});
socket.on('disconnect', function () { // Disconnection of the client
users -= 1;
// reloadUsers();
if (pseudoSet(socket)) {
var pseudo;
socket.get('pseudo', function(err, name) {
pseudo = name;
});
var index = pseudoArray.indexOf(pseudo);
pseudo.slice(index - 1, 1);
}
});
});
};
The whole part of socket.io code has to go outside external.chat function. Socket IO has to bind with the http/app server, you should not handle it within each request.
the messages are firing the number of times = the sequence of connection of the client
What essentially happening is, each time a new request arrives you are registering a event handler for message, hence it is fired as many times as the you have accessed chat URL.
io.socket.on('message', function (data) {...})
So I had the same problem. The solution is to close all your listeners on the socket.on('disconnect') event, this is what my code looks like -
socket.on('disconnect', function () {
socket.removeAllListeners('send message');
socket.removeAllListeners('disconnect');
io.removeAllListeners('connection');
});
Might not need to call it on disconnect, not sure but I do it anyway.
I think this misbehavior is because you are attempting to use one of the handful of built-in/reserved event names "message" as an application-specific message. To confirm, change your event name to "message2" or something else and see if the problem goes away. I believe at least "connect", "disconnect", and "message" are reserved. https://github.com/LearnBoost/socket.io/wiki/Exposed-events
Link: https://socket.io/docs/v3/listening-to-events/#socketoffeventname-listener
Please use socket.off method to removes the specified listener from the listener array for the event named eventName.
socket.off("message").on("message", this.UpdateChat);
Restarting the server can be responsible for several identical event listeners on the client side. If the client has not reloaded (restarted) you have to make sure that the old event listeners are deleted on the client side when establishing a new connection. You can do that with
io.socket.removeAllListeners()
SOCKET.IO v3.x
I don't really know why reserve event in socket.io are firing up multiple times, hence I make a logic that fits to our needs and address this problem,
I simply create a global mutable value that could change everytime the 'disconnect' fire up, here's the logic I did
let ACTIVE_USERS = []; //assume the propery of an object is { name, socket_id }
const connections = (socket) => {
socket.on("disconnect", (reason) => {
//lets check how many times it fires up
console.log('YOW', reason);
//mutate the ACTIVE_USERS
ACTIVE_USERS = ACTIVE_USERS .filter(user => {
//lets figure it out if this thing helps us
if(user.socket_id === socket.id){
console.log('HEY!!!');
socket.broadcast.emit('disconnected-user',[
message: `${user.name} has been disconnected`
})
}
})
});
}
and the result of it is here