moltarze
moltarze

Reputation: 1501

Socket.IO socket protection without authentication

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:

  1. I don't want the user to have to authenticate themselves - anybody should be able to play!

  2. I want to keep the click rate around 15 clicks per second, if possible.

  3. 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?

Upvotes: 1

Views: 1093

Answers (1)

Elliot Nelson
Elliot Nelson

Reputation: 11557

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).

Upvotes: 1

Related Questions