How can I prevent malicious use of my sockets?

2019-04-11 10:16发布

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!

5条回答
姐就是有狂的资本
2楼-- · 2019-04-11 10:25

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

查看更多
劳资没心,怎么记你
3楼-- · 2019-04-11 10:26

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

查看更多
做个烂人
4楼-- · 2019-04-11 10:38

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.

查看更多
倾城 Initia
5楼-- · 2019-04-11 10:39

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

查看更多
我只想做你的唯一
6楼-- · 2019-04-11 10:40

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

查看更多
登录 后发表回答