У меня проблема с тем, кто продолжает использовать мой сайт для ставок с ботом. Он может (предположительно) использовать бота, чтобы нажимать кнопку «Roll» несколько раз очень быстро и получать одинаковые номера рулонов.
Кнопка roll, использующая функцию для работы. Вот эта функция:
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(); }
Затем эта функция приводит к файлу php. Вот его заголовок:
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);
Как вы можете видеть с самого начала, я попытался создать работу, создав случайную строку, исключающую этот пробег ($ setstring), сохраняя его, а затем сравнивая его с самим собой. Однако каким-то образом он сумел запустить его достаточно быстро, чтобы пройти мимо этого.
$ newseed – это переменная с номером рулона. Как вы можете видеть, каждый из них генерируется каждый раз во время выполнения. Я вообще путаюсь, как он может это сделать, поскольку я думал, что каждый php-файл работает отдельно.
Может ли кто-нибудь помочь представить некоторые идеи или решения! Например, я предложил инкапсуляцию транзакций, но не знаю, как реализовать. Спасибо, что нашли время.
Я предполагаю, что эта атака работает из-за многопоточности. При запуске многих запросов ваш код будет вести себя беспорядочно из-за случайного чередования.
Простым примером является банк:
Предположим, вы хотите вычесть 20 долларов США из своей учетной записи:
$amount = q("SELECT * FROM accounts WHERE id = $account_id"); q("UPDATE accounts SET amount = $amount - 20 WHERE id = $account_id");
Если вы начинаете с $ 100, и вы запускаете этот код дважды, вы ожидаете, что получите в итоге 60 долларов. Но, если чередование потоков происходит между вызовом select и update, оба потока будут читать 100 долларов, поэтому оба они будут обновлены до 80 долларов.
Вы думаете в правильном направлении с помощью $setstring
но вам нужно что-то более мощное. Вам нужно посмотреть на блокировку.
$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);
Замки могут быть реализованы разными способами. PHP даже имеет некоторые специальные функции и расширения для него. Самый простой способ – использовать блокировку файла.
function acquire_lock($name) { return fopen($name, "rw"); } function release_lock($lock) { fclose($lock); }
Отказ от ответственности: я не проверял это, я считаю, что он должен работать теоретически: p
Вы можете проверить это с помощью скрипта, который делает:
$lock = acquire_lock("foo"); sleep(30); // do nothing for 30 seconds release_lock($lock);
Затем попробуйте запустить другой скрипт, который также пытается получить блокировку foo
. Он должен ждать 30 секунд.
вы также можете реализовать простой debouncer, используя javascript в вашей кнопке roll, или просто реализовать Google captcha