2014-03-01 37 views
7

我怎樣才能防止有人簡單地做節點socket.io,防止氾濫的任何東西?

while(true){client.emit('i am spammer', true)}; 

這肯定被證明是一個問題,當有人衝動崩潰我的節點服務器!

enter image description here

+1

在接收到洪水後會終止連接(在短時間窗口中顯示一定數量的消息)可以接受嗎?當然,他們之後可以重新連接,但這會將問題轉變爲經典的DoS保護領域。 – zinga

+0

http://stackoverflow.com/questions/16719749/how-can-i-prevent-malicious-use-of-my-sockets/20967971#20967971 – GiveMeAllYourCats

+1

WebSockets,畢竟,只是插座。典型的通過防火牆的DoS保護可能就足夠了。你也可以實施類似節流套接字的東西,如果套接字仍然處於高流量狀態,那麼加班只會被丟棄。套接字具有會話關聯性,所以它實際上使得監視和限制套接字非常容易。 – tsturzl

回答

3

就像tsrurzl說你需要實現一個rate limiter(節流插座)。

以下代碼示例僅在您的套接字返回緩衝區(而不是字符串)時才能可靠地工作。代碼示例假定您將首先調用addRatingEntry(),然後立即調用evalRating()。否則,在evalRating()根本沒有被調用的情況下,你會冒內存泄漏的風險。

var rating, limit, interval; 

rating = []; // rating: [*{'timestamp', 'size'}] 
limit = 1048576; // limit: maximum number of bytes/characters. 
interval = 1000; // interval: interval in milliseconds. 
// Describes a rate limit of 1mb/s 

function addRatingEntry (size) { 
    // Returns entry object. 
    return rating[(rating.push({ 
     'timestamp': Date.now(), 
     'size': size 
    }) - 1); 
} 

function evalRating() { 
// Removes outdated entries, computes combined size, and compares with limit variable. 
// Returns true if you're connection is NOT flooding, returns false if you need to disconnect. 
    var i, newRating, totalSize; 
    // totalSize in bytes in case of underlying Buffer value, in number of characters for strings. Actual byte size in case of strings might be variable => not reliable. 
    newRating = []; 
    for (i = rating.length - 1; i >= 0; i -= 1) { 
     if ((Date.now() - rating[i].timestamp) < interval) { 
      newRating.push(rating[i]); 
     } 
    } 
    rating = newRating; 

    totalSize = 0; 
    for (i = newRating.length - 1; i >= 0; i -= 1) { 
     totalSize += newRating[i].timestamp; 
    } 

    return (totalSize > limit ? false : true); 
} 

// Assume connection variable already exists and has a readable stream interface 
connection.on('data', function (chunk) { 
    addRatingEntry(chunk.length); 
    if (evalRating()) { 
     // Continue processing chunk. 
    } else { 
     // Disconnect due to flooding. 
    } 
}); 

您可以添加額外的檢查,如檢查尺寸參數是否真的是一個號碼等

附錄:確保等級,限制和時間間隔變量包含(在關閉)每個連接,並且它們沒有定義全局速率(每個連接操縱相同的評級)。

1

我實現了一個小洪水功能,並不完美(見下面的改進),但是當他做出很多請求時,它會斷開用戶的連接。

// Not more then 100 request in 10 seconds 
let FLOOD_TIME = 10000; 
let FLOOD_MAX = 100; 

let flood = { 
    floods: {}, 
    lastFloodClear: new Date(), 
    protect: (io, socket) => { 

     // Reset flood protection 
     if(Math.abs(new Date() - flood.lastFloodClear) > FLOOD_TIME){ 
      flood.floods = {}; 
      flood.lastFloodClear = new Date(); 
     } 

     flood.floods[socket.id] == undefined ? flood.floods[socket.id] = {} : flood.floods[socket.id]; 
     flood.floods[socket.id].count == undefined ? flood.floods[socket.id].count = 0 : flood.floods[socket.id].count; 
     flood.floods[socket.id].count++; 

     //Disconnect the socket if he went over FLOOD_MAX in FLOOD_TIME 
     if(flood.floods[socket.id].count > FLOOD_MAX){ 
      console.log('FLOODPROTECTION ', socket.id) 
      io.sockets.connected[socket.id].disconnect(); 
      return false; 
     } 

     return true; 
    } 
} 

exports = module.exports = flood; 

,然後用它是這樣的:

let flood = require('../modules/flood') 

// ... init socket io... 

socket.on('message', function() { 
    if(flood.protect(io, socket)){ 
     //do stuff 
    } 
}); 

改進會是這樣,添加旁邊的計數,他得到了多久disconneted另一個值,然後創建一個banlist並且不要讓他接了。另外,當用戶刷新頁面時,他會得到一個新的socket.id,因此可能在此處使用一個唯一的cookie值而不是socket.id。