<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/papers/831

                    0x00 背景


                    一段簡單的購買程序,看起來沒有任何問題。

                    剩余余額、商品庫存、購買權限等判斷面面俱到,從頭到腳包裝的嚴嚴實實。

                    但是為何人一多就頻頻漏點吶?何解?

                    0x01 問題分析


                    還是以商城購買為例,商城網站是web程序和數據庫兩部分,業務處理流程:

                    #!shell
                    用戶金額是否大于商品價格—>商品庫存是否充足—>購買操作:生成訂單—>扣除用戶金額—>商品庫存減1
                    

                    2013122510571541794.png

                    流程的每一部分都是web與數據庫打交道,查詢或者操作數據庫。

                    程序示例(PHP+MySQL)

                    #!php
                    $goods=$db->FirstRow("SELECT * FROM ".Tb('goods')." WHERE goods_id='{$goods_id}'");
                    if(empty($goods)) ShowError('商品不存在');
                    /* 金額是否充足 */
                    if($user->money<$goods['price'])  ShowError('金額不足,請充值');
                    /* 商品庫存 */
                    if($goods['num']==0)  ShowError('庫存不足');
                    /* 購買操作 begin */
                    //生成訂單
                    CreateOrder($goods,$user,time());
                    $user->Update('money'=>$user->money-$goods['price']); //用戶金額減少
                    $db->Execute("UPDATE ".Tb('goods')." SET num=num-1 WHERE goods_id='{$goods_id}'");//商品庫存-1
                    ShowSuccess('購買成功');
                    /* 購買操作 end */
                    

                    正常來看這個業務處理是沒有問題的,下面想象下多人同時購買(并發請求,如秒殺活動)的情境可能會引發的問題?

                    如果一個用戶同時有兩次購買請求,一次購買已進行到添加訂單但未扣除用戶金額,另一次購買在第一步用戶金額判斷便不準確了。

                    當商品庫存僅為1時,同時有多個請求,而當前沒有一個請求走到商品庫存減少位置,多次購買都能成功,而商城卻無貨可發。

                    2013122510574019546.png

                    總結來說,當有大量的購買操作同時進行,如果數據庫的處理速度跟不上程序的請求速度,就會出現判斷不準確的問題,造成用戶以單個商品的金額購買多個商品、某些用戶付款了但得不到商品等,算是一個安全風險。

                    0x02 解決方案:


                    核心思想:將一次業務處理流程(如購買操作)作為一個最小操作單元,同一時間只能有一個操作。

                    1.  整個操作加內存鎖。如在memcache里,開始購買時設置購買狀態為進行中,購買結束后清除購買狀態,程序開始時即從memcache里判斷是否有正在進行的購買操作,如有則退出。
                    2.  限制每個用戶的購買間隔,如10秒內僅允許購買一次,最好也是放在內存里。
                    3.  當然,優化數據庫及程序以加快處理速度也是有必要的。
                    

                    解決方案程序示例(PHP+MySQL+Memcached)

                    #!php
                    /**
                     * 通過memcache解決并發購買問題
                     */
                    $goods=$db->FirstRow("SELECT * FROM ".Tb('goods')." WHERE goods_id='{$goods_id}'");
                    if(empty($goods)) ShowError('商品不存在');
                    $mmc=memcache_init();
                    $lastBuyTime=$mmc->get('lastBuyTime_'.$user->userId);
                    if($lastBuyTime>0 && $lastBuyTime>time()-10)  ShowError('10秒內只能進行一次購買');
                    $buying=$mmc->get('buying');
                    if($buying==1)  ShowError('有正在進行的購買,請稍候');
                    /* 金額是否充足 */
                    if($user->money<$goods['price'])  ShowError('金額不足,請充值');
                    /* 商品庫存 */
                    if($goods['num']==0)  ShowError('庫存不足');
                    /* 購買操作 begin */
                    //生成訂單
                    CreateOrder($goods,$user,time());
                    $user->Update('money'=>$user->money-$goods['price']); //用戶金額減少
                    $db->Execute("UPDATE ".Tb('goods')." SET num=num-1 WHERE goods_id='{$goods_id}'");//商品庫存-1
                    /* 購買操作 end */
                    $mmc->set('buying',0);
                    $mmc->set('lastBuyTime_'.$user->userId,time());
                    ShowSuccess('購買成功');
                    

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频