Jump to content

Rate Limiting in PHP Websockets


Monkuar

Recommended Posts


function wsProcessClientMessage($clientID, $opcode, &$data, $dataLength) {
global $wsClients;
 
// check opcodes
if ($opcode == WS_OPCODE_PING) {
// received ping message
return wsSendClientMessage($clientID, WS_OPCODE_PONG, $data);
}
elseif ($opcode == WS_OPCODE_PONG) {
// received pong message (it's valid if the server did not send a ping request for this pong message)
if ($wsClients[$clientID][4] !== false) {
$wsClients[$clientID][4] = false;
}
}
elseif ($opcode == WS_OPCODE_CLOSE) {
// received close message
if (substr($data, 1, 1) !== false) {
$array = unpack('na', substr($data, 0, 2));
$status = $array['a'];
}
else {
$status = false;
}
 
if ($wsClients[$clientID][2] == WS_READY_STATE_CLOSING) {
// the server already sent a close frame to the client, this is the client's close frame reply
// (no need to send another close frame to the client)
$wsClients[$clientID][2] = WS_READY_STATE_CLOSED;
}
else {
// the server has not already sent a close frame to the client, send one now
wsSendClientClose($clientID, WS_STATUS_NORMAL_CLOSE);
}
 
wsRemoveClient($clientID);
}
elseif ($opcode == WS_OPCODE_TEXT || $opcode == WS_OPCODE_BINARY) {
// received text or binary message
if (function_exists('wsOnMessage')) wsOnMessage($clientID, $data, $dataLength, $opcode == WS_OPCODE_BINARY);
}
else {
// unknown opcode
return false;
}
 
return true;
}
 
 
 

 

Okay, this is the code that processes the client messages.   It's extracted from here: http://code.google.com/p/phpwebsocket/ and I use it.

 

My problem is. I call a mysql select on a function via my websockets using this:

 

 

 


cb.socket.send('LOOTITEM 2213123');

 

 

 

 

(The CB class just connects it to the socket and sends it off)

 

But I have a problem.  I can crash my own server, just by going into Google Chromes console tab and spamming this:

 

 


cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');cb.socket.send('LOOTITEM 2213123');

 

Then I press enter, and look at my console. And a shit ton of queries are instantly checked and then it pops and says:

 

33996f94672e498f0fd461dfa63989cb.png

 

So, this is way bad. Because people can just do this and crash the server. I need to limit the requests to atleast 1 second. Any idea?

 

Edited by Monkuar
Link to comment
Share on other sites

Sounds like you have a bug in your wsBuildClientFrame function which is causing it to go into a recursion loop. You should address that issue before adding rate limiting.

 

To add rate limiting, you count the # of requests along with keeping track of a timestamp. On each request compare the current # of requests to the # at the last timestamp and see if it's above your limit. If so, deny the request.

 

Something like this

//$lastTimestamp = timestamp of last check
//$lastCount = whatever $count was at last check
$now = time();
$count++; 
$timeDiff = $now - $lastTimestamp;
if ($timeDiff > 1){ //Ensure timestamps have changed
   $countDiff = $count - $lastCount;
   $lastTimestamp = $now;
   $lastCount = $count;

   if ($countDiff/$timeDiff > 5){ //if more than 5 requests per second
      denyRequest();
   }
}
  • Like 1
Link to comment
Share on other sites

Sounds like you have a bug in your wsBuildClientFrame function which is causing it to go into a recursion loop. You should address that issue before adding rate limiting.

 

To add rate limiting, you count the # of requests along with keeping track of a timestamp. On each request compare the current # of requests to the # at the last timestamp and see if it's above your limit. If so, deny the request.

 

Something like this

//$lastTimestamp = timestamp of last check
//$lastCount = whatever $count was at last check
$now = time();
$count++; 
$timeDiff = $now - $lastTimestamp;
if ($timeDiff > 1){ //Ensure timestamps have changed
   $countDiff = $count - $lastCount;
   $lastTimestamp = $now;
   $lastCount = $count;

   if ($countDiff/$timeDiff > 5){ //if more than 5 requests per second
      denyRequest();
   }
}

 I see.

 

And for the denyRequest function just simply return or exit out the user?

 

And this issue wouldn't be prevalent if I were using a more modern approach, like socket.io/node.js correct?

Link to comment
Share on other sites

Sounds like you have a bug in your wsBuildClientFrame function which is causing it to go into a recursion loop. You should address that issue before adding rate limiting.

 

To add rate limiting, you count the # of requests along with keeping track of a timestamp. On each request compare the current # of requests to the # at the last timestamp and see if it's above your limit. If so, deny the request.

 

Something like this

//$lastTimestamp = timestamp of last check
//$lastCount = whatever $count was at last check
$now = time();
$count++; 
$timeDiff = $now - $lastTimestamp;
if ($timeDiff > 1){ //Ensure timestamps have changed
   $countDiff = $count - $lastCount;
   $lastTimestamp = $now;
   $lastCount = $count;

   if ($countDiff/$timeDiff > 5){ //if more than 5 requests per second
      denyRequest();
   }
}

 

 

Sorry to bump this thread, but I got a working version:

function wsOnMessage($clientID, $message, $messageLength, $binary) {
global $servername, $dbuser, $dbpassword, $dbname, $users, $db;
 
// anti flood protection
if($_SESSION['last_session_request'] > time() - 1){
// users will be redirected to this page if it makes requests faster than 2 seconds
echo "Limit Reached... Simmer down!";
return false;
}else{
 
echo $_SESSION['last_session_request'];
}

I got a working version working here:

 

My question is: That return false works but it will stop the whole script? Which if other users are using will stop them as well right? How do I only target the specific client and user instead?

 

Also, should I use microtime instead? 

 

You know, I can use "wsClose($clientID);" to kick them off. But what's stopping them from just refreshing?

Edited by Monkuar
Link to comment
Share on other sites

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.