成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

涉及錢時,必須考慮防刷、限量和防重

開發 前端
對于短信驗證碼這種開放接口,程序邏輯內需要有防刷邏輯。好的防刷邏輯是,對正常使用的用戶毫無影響,只有疑似異常使用的用戶才會感受到。對于短信驗證碼,有如下 4 種可行的方式來防刷。

任何涉及錢的代碼必須要考慮防刷、限量和防重,要做好安全兜底。涉及錢的代碼,主要有以下三類。

第一,代碼本身涉及有償使用的三方服務。如果因為代碼本身缺少授權、用量控制而被利用導致大量調用,勢必會消耗大量的錢,給公司造成損失。有些三方服務可能采用后付款方式的結算,出現問題后如果沒及時發現,下個月結算時就會收到一筆數額巨大的賬單。

第二,代碼涉及虛擬資產的發放,比如積分、優惠券等。雖然說虛擬資產不直接對應貨幣,但一般可以在平臺兌換具有真實價值的資產。比如,優惠券可以在下單時使用,積分可以兌換積分商城的商品。所以從某種意義上說,虛擬資產就是具有一定價值的錢,但因為不直接涉及錢和外部資金通道,所以容易產生隨意性發放而導致漏洞。

第三,代碼涉及真實錢的進出。比如,對用戶扣款,如果出現非正常的多次重復扣款,小則用戶投訴、用戶流失,大則被相關管理機構要求停業整改,影響業務。又比如,給用戶發放返現的付款功能,如果出現漏洞造成重復付款,涉及 B 端的可能還好,但涉及 C 端用戶的重復付款可能永遠無法追回。

前段時間拼多多一夜之間被刷了大量 100 元無門檻優惠券的事情,就是限量和防刷出了問題。

今天,我們就通過三個例子,和你說明如何在代碼層面做好安全兜底。

開放平臺資源的使用需要考慮防刷

我以真實遇到的短信服務被刷案例,和你說說防刷。

有次短信賬單月結時發現,之前每個月是幾千元的短信費用,這個月突然變為了幾萬元。查數據庫記錄發現,之前是每天發送幾千條短信驗證碼,從某天開始突然變為了每天幾萬條,但注冊用戶數并沒有激增。顯然,這是短信接口被刷了。

我們知道,短信驗證碼服務屬于開放性服務,由用戶側觸發,且因為是注冊驗證碼所以不需要登錄就可以使用。如果我們的發短信接口像這樣沒有任何防刷的防護,直接調用三方短信通道,就相當于“裸奔”,很容易被短信轟炸平臺利用:

@GetMapping("wrong")

publicvoid wrong() {

    sendSMSCaptcha("13600000000");

}

privatevoid sendSMSCaptcha(String mobile) {

//調用短信通道

}

對于短信驗證碼這種開放接口,程序邏輯內需要有防刷邏輯。好的防刷邏輯是,對正常使用的用戶毫無影響,只有疑似異常使用的用戶才會感受到。對于短信驗證碼,有如下 4 種可行的方式來防刷。

第一種方式,只有固定的請求頭才能發送驗證碼。

也就是說,我們通過請求頭中網頁或 App 客戶端傳給服務端的一些額外參數,來判斷請求是不是 App 發起的。其實,這種方式“防君子不防小人”。

比如,判斷是否存在瀏覽器或手機型號、設備分辨率請求頭。對于那些使用爬蟲來抓取短信接口地址的程序來說,往往只能抓取到 URL,而難以分析出請求發送短信還需要的額外請求頭,可以看作第一道基本防御。

第二種方式,只有先到過注冊頁面才能發送驗證碼。

對于普通用戶來說,不管是通過 App 注冊還是 H5 頁面注冊,一定是先進入注冊頁面才能看到發送驗證碼按鈕,再點擊發送。我們可以在頁面或界面打開時請求固定的前置接口,為這個設備開啟允許發送驗證碼的窗口,之后的請求發送驗證碼才是有效請求。

這種方式可以防御直接繞開固定流程,通過接口直接調用的發送驗證碼請求,并不會干擾普通用戶。

第三種方式,控制相同手機號的發送次數和發送頻次。

除非是短信無法收到,否則用戶不太會請求了驗證碼后不完成注冊流程,再重新請求。因此,我們可以限制同一手機號每天的最大請求次數。驗證碼的到達需要時間,太短的發送間隔沒有意義,所以我們還可以控制發送的最短間隔。比如,我們可以控制相同手機號一天只能發送 10 次驗證碼,最短發送間隔 1 分鐘。

第四種方式,增加前置圖形驗證碼。

短信轟炸平臺一般會收集很多免費短信接口,一個接口只會給一個用戶發一次短信,所以控制相同手機號發送次數和間隔的方式不夠有效。這時,我們可以考慮對用戶體驗稍微有影響,但也是最有效的方式作為保底,即將彈出圖形驗證碼作為前置。

除了圖形驗證碼,我們還可以使用其他更友好的人機驗證手段(比如滑動、點擊驗證碼等),甚至是引入比較新潮的無感知驗證碼方案(比如,通過判斷用戶輸入手機號的打字節奏,來判斷是用戶還是機器),來改善用戶體驗。

此外,我們也可以考慮在監測到異常的情況下再彈出人機檢測。比如,短時間內大量相同遠端 IP 發送驗證碼的時候,才會觸發人機檢測。

總之,我們要確保,只有正常用戶經過正常的流程才能使用開放平臺資源,并且資源的用量在業務需求合理范圍內。此外,還需要考慮做好短信發送量的實時監控,遇到發送量激增要及時報警。

接下來,我們一起看看限量的問題。

虛擬資產并不能憑空產生無限使用

虛擬資產雖然是平臺方自己生產和控制,但如果生產出來可以立即使用就有立即變現的可能性。比如,因為平臺 Bug 有大量用戶領取高額優惠券,并立即下單使用。

在商家看來,這很可能只是一個用戶支付的訂單,并不會感知到用戶使用平臺方優惠券的情況;同時,因為平臺和商家是事后結算的,所以會馬上安排發貨。而發貨后基本就不可逆了,一夜之間造成了大量資金損失。

我們從代碼層面模擬一個優惠券被刷的例子。

假設有一個 CouponCenter 類負責優惠券的產生和發放。如下是錯誤做法,只要調用方需要,就可以憑空產生無限的優惠券:

@Slf4j

publicclass CouponCenter {

    //用于統計發了多少優惠券

    AtomicInteger totalSent = new AtomicInteger(0);

    public void sendCoupon(Coupon coupon) {

        if (coupon != null)

            totalSent.incrementAndGet();

    }

    public int getTotalSentCoupon() {

        return totalSent.get();

    }

    //沒有任何限制,來多少請求生成多少優惠券

    public Coupon generateCouponWrong(long userId, BigDecimal amount)              {

        returnnew Coupon(userId, amount);

    }

}

這樣一來,使用 CouponCenter 的 generateCouponWrong 方法,想發多少優惠券就可以發多少:

@GetMapping("wrong")

public int wrong() {

    CouponCenter couponCenter = new CouponCenter();

    //發送10000個優惠券

    IntStream.rangeClosed(1, 10000).forEach(i -> {

        Coupon coupon = couponCenter.generateCouponWrong(1L, new BigDecimal("100"));

        couponCenter.sendCoupon(coupon);

    });

    return couponCenter.getTotalSentCoupon();

}

更合適的做法是,把優惠券看作一種資源,其生產不是憑空的,而是需要事先申請,理由是:

虛擬資產如果最終可以對應到真實金錢上的優惠,那么,能發多少取決于運營和財務的核算,應該是有計劃、有上限的。引言提到的無門檻優惠券,需要特別小心。有門檻優惠券的大量使用至少會帶來大量真實的消費,而使用無門檻優惠券下的訂單,可能用戶一分錢都沒有支付。

即使虛擬資產不值錢,大量不合常規的虛擬資產流入市場,也會沖垮虛擬資產的經濟體系,造成虛擬貨幣的極速貶值。有量的控制才有價值。

資產的申請需要理由,甚至需要走流程,這樣才可以追溯是什么活動需要、誰提出的申請,程序依據申請批次來發放。

接下來,我們按照這個思路改進一下程序。

首先,定義一個 CouponBatch 類,要產生優惠券必須先向運營申請優惠券批次,批次中包含了固定張數的優惠券、申請原因等信息:

//優惠券批次

@Data

publicclass CouponBatch {

    privatelong id;

    private AtomicInteger totalCount;

    private AtomicInteger remainCount;

    private BigDecimal amount;

    private String reason;

}

在業務需要發放優惠券的時候,先申請批次,然后再通過批次發放優惠券:

@GetMapping("right")

public int right() {

    CouponCenter couponCenter = new CouponCenter();

    //申請批次    

    CouponBatch couponBatch = couponCenter.generateCouponBatch();

    IntStream.rangeClosed(1, 10000).forEach(i -> {

        Coupon coupon = couponCenter.generateCouponRight(1L, couponBatch);

        //發放優惠券

        couponCenter.sendCoupon(coupon);

    });

    return couponCenter.getTotalSentCoupon();

}

可以看到,generateCouponBatch 方法申請批次時,設定了這個批次包含 100 張優惠券。在通過 generateCouponRight 方法發放優惠券時,每發一次都會從批次中扣除一張優惠券,發完了就沒有了:

public Coupon generateCouponRight(long userId, CouponBatch couponBatch) {

    if (couponBatch.getRemainCount().decrementAndGet() >= 0) {

        returnnew Coupon(userId, couponBatch.getAmount());

    } else {

        log.info("優惠券批次 {} 剩余優惠券不足", couponBatch.getId());

        returnnull;

    }

}



public CouponBatch generateCouponBatch() {

    CouponBatch couponBatch = new CouponBatch();

    couponBatch.setAmount(new BigDecimal("100"));

    couponBatch.setId(1L);

    couponBatch.setTotalCount(new AtomicInteger(100));

    couponBatch.setRemainCount(couponBatch.getTotalCount());

    couponBatch.setReason("XXX活動");

    return couponBatch;

}

這樣改進后的程序,一個批次最多只能發放 100 張優惠券:


因為是 Demo,所以我們只是憑空 new 出來一個 Coupon。在真實的生產級代碼中,一定是根據 CouponBatch 在數據庫中插入一定量的 Coupon 記錄,每一個優惠券都有唯一的 ID,可跟蹤、可注銷。

最后,我們再看看防重。

錢的進出一定要和訂單掛鉤并且實現冪等

涉及錢的進出,需要做好以下兩點。

第一,任何資金操作都需要在平臺側生成業務屬性的訂單,可以是優惠券發放訂單,可以是返現訂單,也可以是借款訂單,一定是先有訂單再去做資金操作。同時,訂單的產生需要有業務屬性。業務屬性是指,訂單不是憑空產生的,否則就沒有控制的意義。比如,返現發放訂單必須關聯到原先的商品訂單產生;再比如,借款訂單必須關聯到同一個借款合同產生。

第二,一定要做好防重,也就是實現冪等處理,并且冪等處理必須是全鏈路的。這里的全鏈路是指,從前到后都需要有相同的業務訂單號來貫穿,實現最終的支付防重。

關于這兩點,你可以參考下面的代碼示例:

//錯誤:每次使用UUID作為訂單號

@GetMapping("wrong")

publicvoid wrong(@RequestParam("orderId") String orderId) {

    PayChannel.pay(UUID.randomUUID().toString(), "123", new BigDecimal("100"));

}

//正確:使用相同的業務訂單號

@GetMapping("right")

publicvoid right(@RequestParam("orderId") String orderId) {

    PayChannel.pay(orderId, "123", new BigDecimal("100"));

}

//三方支付通道

publicclass PayChannel {

    publicstaticvoid pay(String orderId, String account, BigDecimal amount) {

        ...

    }

}

對于支付操作,我們一定是調用三方支付公司的接口或銀行接口進行處理的。一般而言,這些接口都會有商戶訂單號的概念,對于相同的商戶訂單號,無法進行重復的資金處理,所以三方公司的接口可以實現唯一訂單號的冪等處理。

但是,業務系統在實現資金操作時容易犯的錯是,沒有自始至終地使用一個訂單號作為商戶訂單號,透傳給三方支付接口。出現這個問題的原因是,比較大的互聯網公司一般會把支付獨立一個部門。支付部門可能會針對支付做聚合操作,內部會維護一個支付訂單號,然后使用支付訂單號和三方支付接口交互。最終雖然商品訂單是一個,但支付訂單是多個,相同的商品訂單因為產生多個支付訂單導致多次支付。

如果說,支付出現了重復扣款,我們可以給用戶進行退款操作,但給用戶付款的操作一旦出現重復付款,就很難把錢追回來了,所以更要小心。

這,就是全鏈路的意義,從一開始就需要先有業務訂單產生,然后使用相同的業務訂單號一直貫穿到最后的資金通路,才能真正避免重復資金操作。

重點回顧

今天,我從安全兜底聊起,和你分享了涉及錢的業務最需要做的三方面工作,防刷、限量和防重。

第一,使用開放的、面向用戶的平臺資源要考慮防刷,主要包括正常使用流程識別、人機識別、單人限量和全局限量等手段。

第二,虛擬資產不能憑空產生,一定是先有發放計劃、申請批次,然后通過批次來生產資產。這樣才能達到限量、有審計、能追溯的目的。

第三,真實錢的進出操作要額外小心,做好防重處理。不能憑空去操作用戶的賬戶,每次操作以真實的訂單作為依據,通過業務訂單號實現全鏈路的冪等控制。

如果程序邏輯涉及有價值的資源或是真實的錢,我們必須有敬畏之心。程序上線后,人是有休息時間的,但程序是一直運行著的,如果產生安全漏洞,就很可能在一夜之間爆發,被大量人利用導致大量的金錢損失。

除了在流程上做好防刷、限量和防重控制之外,我們還需要做好三方平臺調用量、虛擬資產使用量、交易量、交易金額等重要數據的監控報警,這樣即使出現問題也能第一時間發現。

責任編輯:武曉燕 來源: JAVA日知錄
相關推薦

2025-02-28 13:00:00

SpringBoot接口接口安全

2024-06-14 09:30:58

2025-06-12 08:21:22

2022-06-12 06:45:26

高并發防重

2023-12-18 07:37:17

JavaScript防抖節流

2021-04-26 08:54:17

Spring BootSecurity防重登錄

2018-08-06 08:11:26

2025-06-09 01:22:00

2012-11-19 10:02:01

cookie poiscookie防篡改cookie

2024-05-28 09:26:46

2016-11-17 14:52:29

2017-05-01 16:29:40

2012-10-19 09:22:54

2010-06-18 23:01:16

IT管理安防產業H3C

2010-06-21 21:35:28

運維管理安防行業H3C

2010-09-14 19:50:55

2023-07-24 08:00:56

客戶端訪問指定

2012-02-13 17:02:44

內網安全行為監控CISA

2010-06-01 16:25:24

Zabbix報警

2010-01-11 10:46:31

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 国产精品99视频 | 日韩精品成人免费观看视频 | 99精品视频免费在线观看 | 成人免费一区二区三区视频网站 | 日本高清不卡视频 | 日韩日韩日韩日韩日韩日韩日韩 | 亚洲成人久久久 | 999精品在线| 91啪影院 | 91视频国产精品 | 国产精品污污视频 | 亚洲第一成人av | 欧美一级免费黄色片 | 在线观看国产视频 | 97成人免费| 天堂一区二区三区四区 | 毛片综合 | 免费在线看黄视频 | 亚洲人成人一区二区在线观看 | 色欧美片视频在线观看 | 精品综合网 | 国产91成人 | 国产乱码精品一区二区三区中文 | 一区二区久久电影 | 中文一级片 | 国产精品久久久久久久久久久久午夜片 | tube国产 | 亚洲成人一区二区 | 久草在线中文888 | 欧美国产91 | 久久精品成人一区 | 在线视频 亚洲 | 自拍视频网站 | 欧美视频免费在线观看 | 国产欧美日韩在线观看 | 在线看一区二区 | 宅女噜噜66国产精品观看免费 | 精品欧美一区二区三区免费观看 | 久久久精品网站 | 成人在线亚洲 | 人人亚洲|