天天看點

短信驗證碼被刷怎麼辦?java 短信驗證碼防刷政策一 事件簡述二 問題分析三 應急解決方案四 最終解決方案

短信驗證碼被刷怎麼辦?

  • 一 事件簡述
  • 二 問題分析
  • 三 應急解決方案
    • 1 黑名單模式攔截
    • 2 請求驗證攔截
    • 3 應急方案總結
  • 四 最終解決方案
    • 第一步:擷取防火牆帳号密鑰
    • 第二步:下載下傳防火牆伺服器
    • 第三步:前後端接入
    • 檢視防火牆資料

一 事件簡述

這是一件發生在前段時間的事情,當時的情況是這樣的:一個新的功能子產品上線之後,出現短信接口被惡意通路調用的情況,請求數量很大,而且通過檢視短信服務商控制台也發現,短信發送量在飙升,看着統計曲線的增長,緊張的氣氛也漸漸變得更濃,很明顯,事情并不是遇到一個bug那麼簡單,因為牽涉到服務費用,需要立即解決。

當然,接口被惡意通路的這個問題已經解決,是以寫了這篇文章,可以做一下簡單的記錄,并且靜下心來分析一下其中的問題了,看完這個案例,大家也可以一起讨論讨論。

二 問題分析

短信驗證碼被刷怎麼辦?java 短信驗證碼防刷政策一 事件簡述二 問題分析三 應急解決方案四 最終解決方案

這是當時的短信接口日志數量曲線,某一個時間點突然增長了起來并且沒有降下去的意思,通過日志分析發現,攻擊者用的不同IP、不同号碼進行惡意調用,請求量較大,趕緊将事件做了記錄并通知了相關人員,和同僚做了溝通後,大家也都提出了自己的意見:有人說趕緊修改前端功能,發一版新的APP,有人說修改後端代碼,緊急補救一下,也有人說要不要先關停一下服務…在網上技術論壇搜了一下相關問題,好像碰到這種事情的也不少,基本思路都是加驗證碼,做好安全驗證,被攻擊了無可奈何之類的雲雲。

簡單對各個方案做了整理:

  1. 修改url(APP已經上線,暫時無法修改)。
  2. 添加驗證碼驗證(APP已經上線,暫時無法通過這種方式來解決)。
  3. 停掉短信服務(不現實,其他功能子產品也需要調用短信服務,不考慮實施)。
  4. 短信服務商自帶防攻擊,等一段時間,讓攻擊者自己停止攻擊(雖然短信服務商自帶防攻擊,但是依然會出現大量的垃圾請求,而且服務商隻是針對次數和時間做了限制,一段時間後依然會發送短信,是以,危害和損失還是不小的,問題依然急需處理掉,裝鴕鳥是解決不了問題的)

三 應急解決方案

在使用者互動界面攔截請求已經不現實了,因為移動端短時間内是無法立刻更新的,而等待攻擊停止的方案也不可取,選擇逃避和等待是解決不了問題的,是以最終的決定就是修改後端接口邏輯和代碼。找到最關鍵的問題,雖然存在網絡攻擊,但是真正需要立刻解決的是短信服務接口的調用問題,當務之急是修改短信發送接口,盡快止損。

通過讨論和簡單的分析,最終是決定先修改後端邏輯緊急打一個線上更新檔,移動端也做同步修改,等待發版。

1 黑名單模式攔截

由于接口一直被調用,需要緊急處理,減少短信服務費用的損失,是以一開始的出發點放在了手機号碼上,針對手機号碼做驗證,采用黑名單的模式,對于此接口中出現的号碼,在一定次數的請求後就立刻加入到黑名單清單中,再次請求時,如果是黑名單中的号碼,直接傳回錯誤碼,不做任何其他處理,也不會調用短信發送接口,這種方式可能會誤傷到真實使用者,但是情況比較特殊,是以就選擇了這個應急方案,緊急修改了後端代碼,對部分代碼邏輯做了修改,添加手機号碼的黑名單功能。在短信發送子產品中,對号碼進行驗證,如果一段時間内多次請求同一個号碼的話,将号碼存入資料庫視為黑名單中的号碼,不會發送短信。

短信驗證碼被刷怎麼辦?java 短信驗證碼防刷政策一 事件簡述二 問題分析三 應急解決方案四 最終解決方案

攔截了近700個手機号碼,這些号碼中應該很多是空号吧:

短信驗證碼被刷怎麼辦?java 短信驗證碼防刷政策一 事件簡述二 問題分析三 應急解決方案四 最終解決方案

2 請求驗證攔截

上面的方法雖然起到了一定的作用,但是依然無法很好的解決掉問題,為什麼這麼說呢?因為即使利用了黑名單模式,在進入到黑名單清單之前,依然會發送短信,試想一下每分鐘1000次的惡意請求,即使拉黑了其中的一部分号碼,還是會有一部分漏網之魚會被當做正常資料,然後請求短信服務商接口發送短信,這也是一個不小的體量,黑名單模式可以處理一些問題,但是隻能起到微小的作用,還需要進一步修改後端邏輯。

回到大家都提到的用驗證碼做安全驗證,前端雖然無法立即更新添加驗證碼界面和處理邏輯,但是驗證碼的設計就是識别正常請求和非法請求,是以找到一個方法能夠識别請求是否非法即可,并不一定非要添加驗證碼功能。本子產品在設計接口之初,就做了資料傳輸規定,移動端向後端發送請求時,必須在請求頭中放入一些參數,這些參數本來是做分析用的,但是在這裡起到了很大的作用,是以可以在請求對象request上做文章,攻擊請求隻是發送請求到url,攻擊者也隻知道url并不知道請求參數設計,是以針對這點做驗證,應該可以攔截掉所有的惡意請求了,甚至請求都不會到達黑名單驗證環節就已經被處理掉了。

再次修改後端代碼,由請求資訊request對象入手,從請求對象request中提取資料做校檢,甄别是否為正常請求,如果是正常請求,資料中的參數不會為空且參數值是可控的,而惡意虛假請求中則不含有這些參數,是以直接傳回錯誤碼不作處理即可,這個更新檔打上之後,短信服務費用的損失就不會再增加了。

短信驗證碼被刷怎麼辦?java 短信驗證碼防刷政策一 事件簡述二 問題分析三 應急解決方案四 最終解決方案

3 應急方案總結

不管是前端驗證碼,或者這次采取的驗證請求方式,都是一種驗證方式,用來甄别是否為移動端APP發送過來的正常請求,如果不是,就不做處理,通過日志和黑名單資料可以得出結論,短信發送的問題已經解決。

這個事件也說明,安全驗證不能掉以輕心,也不能心存僥幸心理,一旦被心存惡意之人找到漏洞,還是挺難過的。前端驗證沒有完全考慮到,後端驗證攔截也做的不到位,是以出現了這種情況,需要檢讨和反思,而且處理方式也不是特别得當,一開始的黑名單模式并沒有完全杜絕掉短信發送的問題,又去做了後面的補救,當時确實比較緊張,是以想到能用的方法就趕緊用在了修改上面。

為何說驚險和緊張,試想一下:周末剛剛在家裡修整了兩天,周一的早晨,打完卡坐在工位上悠閑的喝着茶,悠悠的打開浏覽器檢視系統日志,忽然發現這個通路量有點大呀,隐隐覺着不對,認真的查了一下發現,接口被攻擊了,而且是短信發送的接口,看着一條條的短信因為攻擊而發送出去,那一條條的短信,是白花花的銀子啊,能不緊張嗎!什麼感覺?吃着火鍋唱着歌,突然就被麻匪給劫了,跟葛大爺一樣,就是那種感覺。

短信驗證碼被刷怎麼辦?java 短信驗證碼防刷政策一 事件簡述二 問題分析三 應急解決方案四 最終解決方案

至于說險勝,是因為雖然暫時解決了短信發送的問題,不會再進一步的造成金錢的損失,卻存在另外一個問題:大量的惡意請求。

四 最終解決方案

使用短信防火牆。 從以下幾個方面概括一下:

新昕科技在交易反欺詐核心上, 通過AI快速學習機制,結合國際領先的裝置指紋技術,首次推出無需圖形驗證碼機制的企業短信防火牆,三步完成下載下傳對接。

短信驗證碼被刷怎麼辦?java 短信驗證碼防刷政策一 事件簡述二 問題分析三 應急解決方案四 最終解決方案

第一步:擷取防火牆帳号密鑰

進入 防火牆控制台,在左側導航欄選擇【網站管理】,進入網站管理頁面,單擊【發到郵箱】接收密鑰。

短信驗證碼被刷怎麼辦?java 短信驗證碼防刷政策一 事件簡述二 問題分析三 應急解決方案四 最終解決方案

第二步:下載下傳防火牆伺服器

前往新昕科技官網,在頂部導航欄選擇【解決方案】>【下載下傳中心】,進入下載下傳中心頁面,找到短信防火牆伺服器安裝包,點選【下載下傳連結】即可下載下傳。

第三步:前後端接入

前端接入

Java 在頁面合适的位置(标簽内)加入以下代碼引入JS檔案:         
           

PHP 在頁面合适的位置(标簽内)加入以下代碼引入JS檔案:

後端接入

Java

修改配置(和業務系統同系統不需要修改):

newxt.ini (存放位置:"\src\resource")

修改參數(fireWareUrl)–>http://localhost:7502 (本地服務路徑)

(isNginx)–>true(有無反向代理)

短信下發

public RetMsg smsSend(HttpServletRequest request, HttpServletResponse response, String clientMobile) {
    RetMsg retMsg = new RetMsg(-1, "系統異常");
    FwClient fwClient = new FwClientImpl();
    try {
        // 1 調用【短信防火牆】短信發送請求
        HashMap < String, Object > paramMap = fwClient.getSendReq(request, clientMobile);
        String jsonReq = fwClient.execReq(paramMap);
        String smsSendRet = fwClient.getRetVaule(jsonReq, "riskResult");
        if("REJECT".equals(smsSendRet)) {
            retMsg.setRet(3);
            retMsg.setMsg("請求過于頻繁");
        }
        else {
            // 業務 TODO
            // 業務調用短信接口 TODO
            // 調用短信接口 結束
            if(smsRetMsg != null && smsRetMsg.getRet() == 0) {
                // 2 調用【短信防火牆】成功結果
                fwClient.execSucc(paramMap);
                logger.debug("send succ");
                retMsg.setRet(0);
                retMsg.setMsg("發送驗證碼成功");
            }
            else {
                // 2 調用【短信防火牆】失敗結果
                SmsVerifyCache.getInstance().remove(clientMobile);
                fwClient.execFail(paramMap);
                retMsg.setRet(-1);
                retMsg.setMsg("發送驗證碼失敗");
            }
        }
    }
    catch(Exception e) {
        for(StackTraceElement elment: e.getStackTrace()) {
            logger.error(elment.toString());
        }
    }
    return retMsg;
}
           

短信驗證

public RetMsg smsVerify(HttpServletRequest request, HttpServletResponse response, String clientMobile, String smsVerifyCode) {
    FwClient fwClient = new FwClientImpl();
    RetMsg retMsg = new RetMsg(-1, "系統異常");
    if(smsVerifyCode == null || smsVerifyCode.isEmpty()) {
        retMsg.setRet(1);
        retMsg.setMsg("輸入驗證碼為空");
    }
    else {
        // 1 調用【短信防火牆】驗證請求
        HashMap < String, Object > paramMap = fwClient.getVerifyReq(request, clientMobile); // 請求防火牆
        String jsonReq = fwClient.execReq(paramMap);
        // 封包處理
        String smsSendRet = fwClient.getRetVaule(jsonReq, "riskResult");
        if("REJECT".equals(smsSendRet)) {
            retMsg.setRet(3);
            retMsg.setMsg("請求過于頻繁");
        }
        // 業務 TODO
        if(cacheSmsVerify != null && cacheSmsVerify.getVerifyCode() != null && !cacheSmsVerify.getVerifyCode().isEmpty()) {
            if(cacheSmsVerify.getVerifyCode().equals(smsVerifyCode)) {
                retMsg.setRet(0);
                retMsg.setMsg("驗證成功");
            }
            else {
                retMsg.setRet(1);
                retMsg.setMsg("驗證碼錯誤");
            }
        }
        else {
            retMsg.setRet(-9);
            retMsg.setMsg("驗證碼逾時");
        }
        if(retMsg.getRet() == 0) {
            // 2 調用【短信防火牆】成功結果
            fwClient.execSucc(paramMap);
        }
        else {
            // 2 調用【短信防火牆】失敗結果
            fwClient.execFail(paramMap);
        }
    }
    return retMsg;
}
           

檢視防火牆資料

通過風控資料看闆,可檢視1-30天的驗證情況、風控攔截情況以及驗證事件觸發的風控政策情況。

進入防火牆控制台,在左側導航欄選擇【風險大盤】,進入風險大盤頁面。

短信驗證碼被刷怎麼辦?java 短信驗證碼防刷政策一 事件簡述二 問題分析三 應急解決方案四 最終解決方案