javascript - Im trying to find out what is allowing users to withdraw twice the amount or more, of what is in their accounts -


i have section of site allows users withdraw dogecoins in accounts wallet. newly launched today. today noticed user depositing , withdrawing multiple times in row. withdrawals double deposits.

luckily have reserve in case happened, , no user accounts affected.

i checked dogechain, , seems there. no transaction malleability, , amounts withdrawn. problem is, time hit dogechain, doubled, pretty means site allowing happen.

the scope of going on, outside of know of php.

when user withdraws, click button opens small withdraw window:

button code:

<a href="#" onclick="javascript:return withdraw();" class="withdraw">withdraw</a></div> 

background javascript withdraw() function:

function _requestwithdraw(amount,valid) {   $.ajax({     'url': './content/ajax/withdraw.php?valid_addr='+valid+'&amount='+amount+'&_unique=<?php echo $unique; ?>',     'datatype': "json",     'success': function(data) {       _message='<br><small>';       if (data['error']=='yes') {                    if (data['content']==0) _message+='entered <?php echo $settings['currency_sign']; ?> address not valid! please, check address , try again.';         else if (data['content']==1) _message+='entered amount not valid. not have enough balance this.';                   }       else {         _message+='amount has been successfuly sended!<br>transaction id: '+data['content'];         refreshbalance();       }       _message+='<br><br><a href="#" class="microbuttons" onclick="javascript:_renewwithdraw();return false;">back</a>';       $("#_withdraw_content").html(_message);                }   }); } var withdrawing; function withdraw() {   withdrawing=false;   $.msgbox({     title:"withdraw funds",     content:"<div id=\"_withdraw_content\"><br><small>enter valid <?php echo $settings['currency_sign']; ?> address:</small><br><input id=\"w_valid_ltc\" type='text' class='l' style='width: 100%;'><br><br><small>enter amount paid-out:</small><br><input id=\"w_amount\" type='text' class='l' style='width: 100px; text-align: center;'><br><br><small><small>min. value: <b>0.001</b> <?php echo $settings['currency_sign']; ?><br>we charge fee <b>0.0002</b> <?php echo $settings['currency_sign']; ?> each withdrawal.</small></small></div>",     type:"info",     opacity:0.8,     buttons: [{ value: "withdraw" }, { value: "cancel" }],     success: function(button) {       if (button=="withdraw" && withdrawing==false) {         w_amount=$("input#w_amount").val();         w_valid=$("input#w_valid_ltc").val();         if (w_amount!='' && w_valid!='') {           $("#_withdraw_content").html('<div style=\"height: 50px;\"></div>&nbsp;&nbsp;&nbsp;<img src="content/images/ajax_loader.gif">');           withdrawing=true;           _requestwithdraw(w_amount,w_valid);         }         else {           alert('one of required fields stayed empty!');         }       }     }   });         return false; } 

they input amount want withdraw, , address send too.

the background happenings follows:

the escaping function:

function prot($hodnota,$max_delka=0) {   $text=mysql_real_escape_string(strip_tags($hodnota));   if ($max_delka!=0)  $vystup=substr($text,0,$max_delka);   else  $vystup=$text;   return $vystup; } 

this portion wallet.php, called withdraw() function window pops on front end:

<?php  $included=true; include '../../inc/db-conf.php'; include '../../inc/wallet_driver.php'; $wallet=new jsonrpcclient($driver_login); include '../../inc/functions.php';  if (empty($_get['amount']) || empty($_get['valid_addr']) || empty($_get['_unique']) || mysql_num_rows(mysql_query("select `id` `sellers` `hash`='".prot($_get['_unique'])."' limit 1"))==0) exit();  $seller=mysql_fetch_array(mysql_query("select `id`,`balance` `sellers` `hash`='".prot($_get['_unique'])."' limit 1"));  $validate=$wallet->validateaddress($_get['valid_addr']); if ($validate['isvalid']==false) {   $error='yes';   $con=0; } else {   if (!is_numeric($_get['amount']) || (double)$_get['amount']>$seller['balance'] || (double)$_get['amount']<0.001) {     $error='yes';     $con=1;   }   else {     $amount=(double)$_get['amount']-0.0002;     $txid=$wallet->sendfrom('',$_get['valid_addr'],$amount);     mysql_query("update `sellers` set `balance`=`balance`-".prot($_get['amount'])." `id`=$seller[id] limit 1");     $error='no';     $con=$txid;   } } $return=array(   'error' => $error,   'content' => $con );  echo json_encode($return); ?> 

thes following function called function _renewwithdraw() (i think) function after have input wallet address , amount of doegcoins , hit "withdraw"

function refreshbalance() {   $("#balance_").html('<img src="content/images/alternative_loader.gif">');   $.ajax({     'url': './content/ajax/request_balance.php?_unique=<?php echo $unique; ?>',     'datatype': "json",     'success': function(data) {       $("#balance_").html(data['balance']);     }   });   return false; 

this page refreshbalance function calls:

<?php  $included=true; include '../../inc/db-conf.php'; include '../../inc/functions.php';  if (empty($_get['_unique']) || mysql_num_rows(mysql_query("select `id` `sellers` `hash`='".prot($_get['_unique'])."' limit 1"))==0) exit(); $balance=mysql_fetch_array(mysql_query("select `balance` `sellers` `hash`='".prot($_get['_unique'])."' limit 1")); $balance_=rtrim(rtrim(sprintf("%0.12f",$balance['balance']),'0'),'.'); $return=array('balance' => $balance_); echo json_encode($return); ?> 

can please me figuring out how, withdrawing double balance, , how stop it. dont have developer @ moment , im pretty sure out of knowledge fix in timely manner.

im guessing either site allowing multiple instances ran @ same time, , isnt checking balance in right order, prevent that.

most importantly, im not sure right question ask is. im hoping @ code, , see better way of doing it.

i think user might have clicked twice or somehow managed initiate 2 ajax request. behaves 2 different threads simultaneously trying withdraw (implement locking mechanism on per user basis). should first execute query , if according conditions send wallet form.

workaround

the quickest way might switch $wallet->sendfrom , mysql_query. positions. let user has 100$ in account , made transaction of 100$ (due bug or thing), becomes 2 ajax request. first thread checks user current balance 100$, enough balance process , calls send wallet information (let take 30 second). meanwhile thread comes in, check current balance (100$ same of first thread read, , haven't returned yet $wallet->sendfrom). send wallet , on return both execute update sql script.

$txid=$wallet->sendfrom('',$_get['valid_addr'],$amount); mysql_query("update `sellers` set `balance`=`balance`-".prot($_get['amount'])." `id`=$seller[id] limit 1"); 

switching these lines reduce chance of error. (you have implement locking mechanism 100% surety).

mysql_query("update `sellers` set `balance`=`balance`-".prot($_get['amount'])." `id`=$seller[id] limit 1"); $txid=$wallet->sendfrom('',$_get['valid_addr'],$amount); 

also try disable button/link initiate withdraw process user enters in mode, , renable once ajax request successful. reduce chance of error front end.

updated :

simulate locking session variable believe php not support nice , clean way thread safety. here workaround using session variable require locking per user basis.(people use flock similar kind of functionality)

separate session variables created each user $_session["variable_name"], can use if current user transaction locked or not.

  1. first step check if $_session["islocked"] true or not, (it not set if user comes first time). if true, means transaction in progress , exit script error.
  2. secondly if not set or contains false (means no transaction in progress) first step assign true. , processed processing of withdraw.
  3. finally when script done processing set session variable false.

here sample code above steps.

<?php session_start();  if (isset($_session["islocked"]) && $_session["islocked"] === true) {     echo "a transaction in progress.";     exit(); } else {      //hold lock     $_session["islocked"] = true;     $_session["lock_time"] =  time(); //current time (can used unlock current user session after time in case of error/bugg or thing)      //perform wallet api calls     echo "processing data";     //you must check if above call success full         //then run update query updating balance.      //release lock     $_session["islocked"] = false; } 

please note: not thread safety way reduce chances of errors.


Comments

Popular posts from this blog

apache - Remove .php and add trailing slash in url using htaccess not loading css -

javascript - jQuery show full size image on click -