My application only needs socket.io to send data from the server to the client. To prevent Denial Of Service attacks, I want to disconnect the client if i tries to emit data. Is this possible?
I've looked at some stackoverflow questions:
force client disconnect from server with socket.io and nodejs
How to protect against distributed denial-of-service attacks in Node.js with Socket.io?
But I've not been able to find a working solution.
There is a option to cache all events (from here Socket.io Client: respond to all events with one handler?).
Then on any event you will just disconnect client on server side.
var socket = io.connect();
var globalEvent = "*";
socket.$emit = function (name) {
if(!this.$events) return false;
for(var i=0;i<2;++i){
if(i==0 && name==globalEvent) continue;
var args = Array.prototype.slice.call(arguments, 1-i);
var handler = this.$events[i==0?name:globalEvent];
if(!handler) handler = [];
if ('function' == typeof handler) handler.apply(this, args);
else if (io.util.isArray(handler)) {
var listeners = handler.slice();
for (var i=0, l=listeners.length; i<l; i++)
listeners[i].apply(this, args);
} else return false;
}
return true;
};
socket.on(globalEvent,function(event){
//Force disconnect
socket.disconnect();
});
This probably isn't very helpful, but the best I've heard of doing is Comet streams. It's an older method, and a lot of people don't like it (myself included), but it's an option for one-way server to client updates.
Essentially, on the client side you have an iframe that connects to the server, and the server sends back a response in the form of a multipart response, occasionally sending back script tags with bits of stuff to execute. So, a trivial (and probably broken) example would be this:
<!--index.html-->
<html>
<body>
<iframe src="/comet/status"></iframe>
</body>
</html>
And then the server code...
// server code (I like Node.JS)
app.get('/comet/status', function (req, res) {
// A function that does a lot of work,
// and occasionally calls a callback with progress
doWork(function (progress) {
res.write('<script>console.log("Progress: " + progress);</script>');
});
res.end();
});
Like I said, this is a pretty incomplete example, but it's a way to accomplish what you're looking for, even if in an older way. Instead of console logging, you'd probably update an element that displays progress.
Related
I've created a simple Node.js app using Express.js and socket.io (available here), where the user clicks a button, and it increments a number on the page. This number is also incremented live among all clients connected to the page. I am using web sockets and socket.io to get the client-server communication and live number updating system.
I am using the flood-protection module to limit socket emits to 5 per second, but this really doesn't make the game very fun because of the low amount of clicks per second you can have, and hackers could just use a setInterval and still make considerable progress automatically, even at such a low rate.
My issue:
I don't want the user to have to authenticate themselves - anybody should be able to play!
I want to keep the click rate around 15 clicks per second, if possible.
I don't want people to be able to send socket messages and automatically click the button from the browser console.
Here's the program:
index.js
var express = require("express");
var http = require("http");
var socketIO = require("socket.io");
var path = require("path");
var fs = require("fs");
var FloodProtection = require("flood-protection").default;
__dirname = path.resolve();
function btoa(str) {
return new Buffer(str, 'latin1').toString('base64');
};
function atob(b64Encoded) {
return new Buffer(b64Encoded, 'base64').toString('latin1');
};
var app = express();
app.get("/", function(req, res){
res.sendFile(__dirname + "/index.html");
});
var temp;
num = temp | parseInt(atob(fs.readFileSync("num.txt"))) | 0
var server = http.createServer(app);
var io = socketIO.listen(server, {log: true});
io.sockets.on("connection", (socket) => {
protector = new FloodProtection({rate: 5, per: 1})
io.sockets.emit("update", num);
socket.on("push", (value) => {
if (protector.check()) {
num++;
temp = num
io.sockets.emit("update", temp);
} else {
io.sockets.emit("update", "You can only click the button five times per second.")
socket.disconnect(2)
setTimeout(()=>{}, 3000)
}
});
socket.on("disconnect", () => {
fs.writeFile("num.txt", btoa(String(temp)), (err) => {
if (err) throw err;
console.log("saved | new num: " + temp);
})
})
});
server.listen(5000);
index.html
<html>
<head>
<title>A Button</title>
</head>
<body>
<button onclick='push();'>Click me!</button>
<p id="out"></p>
</body>
<script type="text/javascript" src="/socket.io/socket.io.js"></script>
<script type="text/javascript">
var variableFromFrontEnd = 2;
var socket = io.connect("/");
socket.on("connect", function() {
socket.on("update", function(val) {
document.getElementById("out").innerHTML = val
});
});
socket.on("disconnect", function() {
setTimeout(()=>{socket.connect();}, 1000);
});
function push() {
if (socket.connected) {
socket.emit("push");
}
}
</script>
</html>
num.txt is a base-64 encoded number.
So, is there a way to be able to do this without significant rate limiting or authentication? Or am I just going to have to use rate limiting?
There's a lot of different ways for users to cheat, and just as many ways to prevent them. The general rule is that you can only "make things harder" (if you're lucky, hard enough that the potential cheater loses interest before succeeding).
For a browser-based game, I would make sure that you are at least ensuring your game gets totally minified/tersed (so your javascript code is as unreadable as possible, and it's more difficult to just call a "click" function directly), and build in checksums in your messages (so the user can't just make socket calls directly to the server).
Once you've done that, you still have to deal with users who generate click events on the element directly with code or a plugin, or users who use a program outside the browser to generate click events above the button repeatedly. Your best defense against this is not to prevent it, but instead to detect it -- probably on the server side, by watching for users that have a sustained click rate that is not humanly possible, and then blowing up their game / temporarily banning their IP / etc.
See a related question Ways to prevent or reduce cheating for more related ideas (this question is about general client-server games, not browser games, but some of the discussion is still useful).
I'm using Redis in my nodejs server, communicating with a browser html/js application. Under conditions I can't identify, my server gets stuck. It throws no errors, keep to be alive and responding, but can't get values from Redis. This is (the simplified version) how the connection with Redis is established.
var Stash = require('redis'),
Sockjs = require('sockjs')
stash = Stash.createClient(some_port,some_host);
sockjs = Sockjs.createServer();
var Instances = {
get: function (id, cb) {
stash.get('something_' + id, cb)
}
sockjs = Sockjs.createServer();
sockjs.on('connection', function(socket) {
socket.on('data', function(data) {
var id = somecorrectid;
Instances.get(id, function(err, instance) { /*Here the magic happens*/ }
}
}
Now, this works perfectly in normal conditions.
When the issue happens, the code between the last brackets isn't executed anymore, no matter how many times I fire again the event triggering the piece of code (that is a post data throught a socket) and from what client. Redis, in the meanwhile, is alive and rocking. Redis-cli is responding. The key searched with the 'get' exist and it is correct in its form. My server is still able to write keys to Redis correctly, but seems it can't read them anymore. This have no sense to me, and it is difficult to investigate or have more informations for I can't get how to replicate the issue. Any clue?
I create a server with Node.js:
var net = require('net');
var PORT = 8181;
var server = net.createServer(
function(socket) {
console.log(this.address());
socket.on('data', function(data) {
var msg = data.toString().replace(/\n$/, '');
console.log('got: ' + msg);
});
process.stdin.on('readable',
function() {
var chunk = process.stdin.read();
if (chunk !== null) {
socket.write(chunk);
}
}
)
socket.write('heyyo\n');
}
)
Now, when multiple connections are coming in, this server sends out the typed in line only to the first connection.
I have two questions:
what is a standard way to handle this, i.e. to store the incoming sockets into an array?
exactly what happens that causes the readable event not to reach the other connections' callback function?
I would highly recommend using a library like socket.io. It makes handling connect/disconnect as well as placing sockets in rooms very simple. Additionally you can get the full list of available rooms and connected sockets through the adapter class it offers. A functional example is available in the docs.
I am having problems getting the sticky-sessions socket.io module to work properly with even a simple example. Following the very minimal example given in the readme (https://github.com/indutny/sticky-session), I am just trying to get this example to work:
var cluster = require('cluster');
var sticky = require('sticky-session');
var http = require('http');
if (cluster.isMaster) {
for (var i = 0; i < 4; i++) {
cluster.fork();
}
Object.keys(cluster.workers).forEach(function(id) {
console.log("Worker running with ID : " +
cluster.workers[id].process.pid);
});
}
if (cluster.isWorker) {
var anotherServer = http.createServer(function(req, res) {
res.end('hello world!');
});
anotherServer.listen(3000);
console.log('http server on 3000');
}
sticky(function() {
var io = require('socket.io')();
var server = http.createServer(function(req, res) {
res.end('socket.io');
});
io.listen(server);
io.on('connection', function onConnect(socket) {
console.log('someone connected.');
socket.on('sync', sync);
socket.on('send', send);
function sync(id) {
socket.join(id);
console.log('someone joined ' + id);
}
function send(id, msg) {
io.sockets.in(id).emit(msg);
console.log('someone sent ' + msg + ' to ' + id);
}
});
return server;
}).listen(3001, function() {
console.log('socket.io server on 3001')
});
and a simple client:
var socket = require('socket.io-client')('http://localhost:3001');
socket.on('connect', function() {
console.log('connected')
socket.emit('sync', 'secret')
});
The workers start up fine. The http servers work fine. But when the client connects, the console logs 'someone connected' and nothing more. The client never fires the on connect event, so I think the upgrade/handshake is failing or something. If anyone can spot what I am doing wrong that would help alot.
Thanks!
#jordyyy : I was facing same issue after googling I have fond answer.
Socket.Io handshaking task complete in more than one request and when you will run on sticky session it means you are using multiple process according to your core.
So handshaking request will distribute on different different process and they can't talk.(not IPC) (They are child process) and most of time connection will be failed/lost.(connection-disconnect event occurs frequently )
So what is solution ? Solution is socketio-sticky-session
Socketio-sticky-session, manage connection on IP based. So when you will request by any client then it will maintain ip address with respect process/worker. So further request will be forward to same process/worker and your connection properly stabilized.
And When you will use redies adapter then you can actually maintain socket
connection data b/w all processes/workers.
For more information
https://github.com/elad/node-cluster-socket.io
(you need some patch on worker_index method, if your server is supporting IPv6)
Just knowledge bytes. :) :)
One more thing, you don't need to fork process. It will be done by sticky session.
This was super old and wasn't really answered when i needed it, but my solution was to drop this bad module and any other super confusing module and just use pub/sub with redis adapter. The only other step was to force transports to websockets, and if that bothers anyone then use something else. For my purposes my solution was simple, readable, didn't mess with the 'typical' socket.io api, and best of all it worked extremely well.
I´m going crazy with socket.io!
Documentation is so bad it's simply not true.
I want to send a feedback to specific client over socket.io
My server side looks like this:
app.get('/upload', requiresLogin, function(request, response) {
response.render('upload/index.jade');
io.sockets.on('connection', function (socket) {
console.log('SOCKET ID ' + socket.id);
io.sockets.socket(socket.id).emit('new', 'hello');
});
});
and the client side looks like this:
$(document).ready(function() {
var socket = io.connect('http://localhost:80/socket.io/socket.io.js');
socket.on('new', function (data) {
console.log(socket.id);
console.log(data);
//$('#state').html(data.status);
});
});
but the client does simply nothing. I have tried nearly everything. Can someone tell me what I am doing wrong, please! :(
to send a message to a specific client save every one that connects to the server in an Object.
var socketio = require('socket.io');
var clients = {};
var io = socketio.listen(app);
io.sockets.on('connection', function (socket) {
clients[socket.id] = socket;
});
then you can later do something like this:
var socket = clients[sId];
socket.emit('show', {});
A couple of ways to send feedback to a specific client over socket.io include:
As stated by pkyeck, save all clients to an Object, so you can send to these specific clients later in your route handlers, e.g.:
var sessionsConnections = {};
sio.on('connection', function (socket) {
// do all the session stuff
sessionsConnections[socket.handshake.sessionID] = socket;
});
or, use socket.io's built in support for rooms - subscribe each client to a room on connection and send via this room within route handlers, e.g.:
sio.on('connection', function (socket) {
// do all the session stuff
socket.join(socket.handshake.sessionID);
// socket.io will leave the room upon disconnect
});
app.get('/', function (req, res) {
sio.sockets.in(req.sessionID).send('Man, good to see you back!');
});
Acknowledgement:
http://www.danielbaulig.de/socket-ioexpress/#comment-1158
Note that both these example solutions assume that socket.io and express have been configured to use the same session store and hence refer to the same session objects. See the links above and below for more details on achieving this:
https://github.com/LearnBoost/socket.io/wiki/Authorizing
2 things
1) You should really place your io.sockets.on(..) outside your app/update route to prevent adding multiple listeners for clients.
2) io.sockets.socket(id); should not be used, it should have been socket.emit('new', 'hello')
In socket.io 1.0, this is how it would work. It may work for lower versions, but I cannot guarantee it.
socket.to(socket_id_here).emit('new', 'hello');
This works because socket.io automatically adds a socket to a room with the socket's id on connection.
Also, if you plan to upgrade to version 1.0, there are a lot of changes to the api, so you'll sometimes have to change some code to make it work in 1.0.
The correct way to do this in Socket.io 1.0+ is:
io.to(users_socket_id).emit('new', 'hello');
You can also substitute a room name for 'users_socket_id' to emit to a specific socket.io room.
First of all, you cannot use socket.id on client side.
And then change the line
var socket = io.connect('http://localhost:80/socket.io/socket.io.js');
to
var socket = io.connect('http://localhost:80/');
I believe io.sockets.socket has been removed and has been a problem in Socket.IO (https://github.com/socketio/socket.io/issues/1618).
You can use io.sockets.connected[socket.id] and store the socket.id to reference with the user via username:
var usernames = {};
usernames[username] = socket.id;
// Sending the message
io.sockets.connected[usernames[username]].emit(...);
I don't see it anywhere on the documentation so I can see how this hasn't been answered. Also, if you don't have any reference via usernames, you can instead try:
users[socket.id] = socket.id;
to duplicate the key and value for easy access.
There is also possibly another way by using io.clients as described below, but I haven't used that method.
OTHER SOLUTION: Send message to specific client with socket.io and node.js
Have you tried using?
var socket = io.connect('http://localhost:80/');
i tired with the latest version of node and socket.io
below i am going to post complete code
<ul id="messages"></ul>
<form action="">
<input id="id1" /><button>Send</button>
</form>
<script src="https://cdn.socket.io/socket.io-1.2.0.js"></script>
<script src="http://code.jquery.com/jquery-1.11.1.js"></script>
<script>
var io = io.connect();
$('form').submit(function(){
io.emit('drumevent', $('#id1').val());
$('#id1').val('');
return false;
});
io.on('drumevent', function(msg){
console.log(msg);
$('#messages').append($('<li></li>').text(msg.message+' quantity = '+msg.quantity));
});
</script>
server side code
var usernames = {};io.on('connection', function(socket){usernames["username"] = socket.id;
socket.on('drumevent', function(msg){
var socketId = socket.id;io.to(socketId).emit('drumevent', data);