Stop bot sends multiple requests quickly. PHP + AJAX

I have a problem with someone who continues to use my bot betting site. He can (presumably) use a bot to hit the Roll button multiple times very quickly and get the same roll numbers.

A roll button using a function to work. This function is:

var rolling=false;
var lastBet=(Date.now()-<?php echo $settings['rolls_mintime']; ?>-1000);
function place(wager,multiplier,bot) {
  if ((rolling==false && (Date.now())>=(lastBet+<?php echo $settings['rolls_mintime']; ?>)) || bot==true) {
    rolling=true;
    lastBet=Date.now();
    $("#betBtn").html('ROLLING');
    if (bot!=true) _stats_content('my_bets');      
    $.ajax({
      'url': './content/ajax/place.php?w='+wager+'&m='+multiplier+'&hl='+under_over+'&_unique=<?php echo $unique; ?>',
      'dataType': "json",
      'success': function(data) {
        if (data['error']=='yes') {
          if (data['data']=='too_small') alert('Error: Your bet is too small.');
          if (data['data']=='invalid_bet') alert('Error: Your balance is too small for this bet.');
          if (data['data']=='invalid_m') alert('Error: Invalid multiplier.');
          if (data['data']=='invalid_hl') alert('Error: Invalid under/over specifier.');
if (data['data']=='invalid_bts') alert('Using bots, tut tut.');
          if (data['data']=='too_big_bet') alert('Error: Your bet is too big. Currently max profit is set at: '+data['under']+' this represents 1% of the invested backroll.');
        }
        else {
          var result=data['result'];
          var win_lose=data['win_lose'];
          if (win_lose==1) winCeremonial();
          else shameCeremonial();
        }

      

This function then leads to the php file. Here is its title:

if (empty($_GET['_unique']) || mysql_num_rows(mysql_query("SELECT `id` FROM `players` WHERE `hash`='".prot($_GET['_unique'])."' LIMIT 1"))==0) exit();

$playerinv=mysql_fetch_array(mysql_query("SELECT `id`,`playcoins`,`time`, `ex`, `server_seed` FROM `players` WHERE `hash`='".prot($_GET['_unique'])."' LIMIT 1"));
$random = base64_encode(openssl_random_pseudo_bytes(10));
$setstring = $random;
mysql_query("UPDATE `players` SET `string` = '$setstring' WHERE `id`=$playerinv[id] LIMIT 1");
$playersec=mysql_fetch_array(mysql_query("SELECT `string` FROM `players` WHERE `hash`='".prot($_GET['_unique'])."' LIMIT 1"));

if ($setstring != $playersec['string']) {
echo json_encode(array('error'=>'yes','data'=>'invalid_bts'));
exit();
}

$newSeed=generateServerSeed();
mysql_query("UPDATE `players` SET `server_seed`='$newSeed' WHERE `id`=$playerinv[id] LIMIT 1");

$settings=mysql_fetch_array(mysql_query("SELECT * FROM `system` LIMIT 1"));

$player=mysql_fetch_array(mysql_query("SELECT * FROM `players` WHERE `hash`='".prot($_GET['_unique'])."' LIMIT 1"));
$player['server_seed_']=$player['server_seed'];
$player['server_seed']=(double)substr($player['server_seed'],27);

      

As you can see from the beginning, I tried to create a job by creating a random string exclusive to this run ($ setstring), storing it, and then comparing it to itself. However, somehow he managed to launch it fast enough to get past it as well.

$ newseed is a variable with a roll number. As you can see, each one is generated every time at runtime. I am generally confused how he can do this as I thought that each php file works separately.

Can anyone please help submit some ideas or solutions! For example, I suggested transaction encapsulation, but don't know how to implement it. Thanks for taking the time.

+3


source to share


2 answers


I am guessing this attack works because of multithreading. When running many queries, your code will behave erratically due to random interleaving.

A simple example is a bank:

Let's say you want to deduct $ 20 from your account:

$amount = q("SELECT * FROM accounts WHERE id = $account_id");
q("UPDATE accounts SET amount = $amount - 20 WHERE id = $account_id");

      

If you start at $ 100 and you run this code twice, you expect to end up with $ 60. But, if interleaving occurs between the call to select and update, both threads will read $ 100, so both of them will be updated to $ 80.

You are thinking in the right direction with $setstring

, but you need something more powerful. You need to look at the lock.

$lock = acquire_lock("foo");
$amount = q("SELECT * FROM accounts WHERE id = $account_id");
q("UPDATE accounts SET amount = $amount - 20 WHERE id = $account_id");
release_lock($lock);

      



Locks can be implemented in a variety of ways. PHP even has some special features and extensions for it. The easiest way is to use a file lock.

function acquire_lock($name) {
    return fopen($name, "rw");
}
function release_lock($lock) {
    fclose($lock);
}

      

Disclaimer: I have not tested this, I believe it should work in theory: p

You can check this with a script that does:

$lock = acquire_lock("foo");
sleep(30); // do nothing for 30 seconds
release_lock($lock);

      

Then try running another script that tries to acquire the lock as well foo

. He has to wait 30 seconds.

+4


source


you can also implement a simple debouncer with javascript in your throw button or a simple google captcha implementation



0


source







All Articles