防止重复执行。 PHP金融应用

时间:2019-02-08 00:31:26

标签: php security data-integrity

如果我有一个脚本会减少重复输入簿记系统中的用户余额,并且恶意用户决定同时在两台不同的计算机(或同一台计算机)上以其帐户执行此脚本,整个事情会运行两次,对不对?这样就简化了我的假设假设。

    $balance = $user->ledger->getBalance(); // returns 5000
    $amount = 3000;
    if ($amount <= $balance) {
        $user->ledger->decrease($amount);
    }

    echo $user->ledger->getBalance(); // echo's 2000

如果脚本一个接一个地运行,则第二次执行将失败,因为帐户中只剩下2000个,并且试图减少3000个。

如果脚本在完全相同的时间同时运行,那么两个余额都不会是5000,并且两个脚本执行都减去3000,从而在分类帐中留下负值吗?

您如何防止此类情况发生?维护此数据库表中的数据完整性至关重要。

1 个答案:

答案 0 :(得分:1)

您所谈论的是race conditions,在财务代码中消除它们非常重要。

遵循“获取/测试/设置”模式的所有内容都会遇到巨大的问题。你不能那样做。

改为采用设置/测试/失败模式。尝试使用原子SQL语句(例如单个操作或事务块)进行推论。如果这样会使天平负移回原处。

例如,这是不好

balance = query("SELECT balance FROM accounts WHERE account_id=?")
balance -= amount
balance = query("UPDATE accounts SET balance=?")

在获取和写入之间可能发生任何事情。

相反,您可以在此查询成功或失败但不能中断的地方执行此操作:

query("UPDATE accounts SET balance=balance-? WHERE account_id=? AND balance>?")

除非有足够的余额,否则该查询将不会运行。结果将得到零行修改。

您还可以通过尝试插入所需的会计交易行,然后检查SUM()以确保原始帐户的余额为零或为正,从而以双分类账簿方式进行记录。如果不是,请使用ROLLBACK放弃交易。不会应用任何更改。

有很多方法来构造那些INSERT语句以使负余额不可能发生,例如INSERT INTO x SELECT ... FROM y,您可以将条件应用于子查询以在出现以下情况时返回零行:余额不足。

相关问题