如何設(shè)計(jì)一個(gè)安全的短信接口?

Java技術(shù)棧
www.javastack.cn
關(guān)注閱讀更多優(yōu)質(zhì)文章
作者:噠噠噠噠打代碼
鏈接:juejin.im/post/6862488906173022216
前言
上一篇文章:你的登錄接口真的安全嗎?
里面,我們主要聊了一下登錄相關(guān)的一些安全風(fēng)險(xiǎn),里面討論到了一個(gè)使用手機(jī)驗(yàn)證碼來(lái)進(jìn)行登錄驗(yàn)證的方式。
但是其實(shí)提供短信驗(yàn)證碼、或者說(shuō)任何可以觸發(fā)短信發(fā)送的接口,都是存在風(fēng)險(xiǎn)的,很有可能被黑產(chǎn)或攻擊者利用。我們今天主要聊一聊短信接口相關(guān)的風(fēng)險(xiǎn)和預(yù)防措施。
背景
短信被刷啦!短信又被刷啦!短信怎么還被刷啊!
很多人在短信服務(wù)剛開始建設(shè)的階段,可能不會(huì)在安全方面考慮太多,理由有很多,比如:“需求這么趕,當(dāng)然是先實(shí)現(xiàn)功能啊”,“業(yè)務(wù)量很小啦,系統(tǒng)就這么點(diǎn)人用,不怕的”等等。
有一些理由雖然有道理,但是該來(lái)的總是會(huì)來(lái)的。前期欠下來(lái)的債,總是要還的。越早還,問(wèn)題就越小,損失就越低。
推薦閱讀:如何設(shè)計(jì)一個(gè)安全的登錄流程
所以大家在安全方面還是要重視。(血淋淋的栗子!)
正文開始
你打敗了99%的人
您本次驗(yàn)證速度打敗了99%的人
最簡(jiǎn)單的方式就是增加驗(yàn)證碼啦,每次用戶主動(dòng)獲取短信前,都需要先完成圖片驗(yàn)證碼/滑動(dòng)驗(yàn)證碼的校驗(yàn)。
示例代碼:
if?not?validate_captcha():??
????return?error('驗(yàn)證碼錯(cuò)誤')??
??????
send_message()??
實(shí)現(xiàn)簡(jiǎn)單,也可以防止一部分攻擊者,但是不管是圖片還是滑動(dòng)驗(yàn)證,都是可以被破解的,一但被破解,那你的短信接口相當(dāng)于對(duì)攻擊者毫不設(shè)防,非常危險(xiǎn)。
沒(méi)有人可以一直發(fā)短信
您的短信發(fā)送已達(dá)上限
一般普通的驗(yàn)證碼類型一般的使用場(chǎng)景都是登錄、修改密碼、注冊(cè)等場(chǎng)景,一般來(lái)說(shuō)都不是高頻操作,所以我們可以針對(duì)單個(gè)用戶和全局做數(shù)量限制:
比如一個(gè)手機(jī)號(hào)1小時(shí)內(nèi)只允許調(diào)用5次,一天內(nèi)只允許調(diào)用20次。
另外再根據(jù)歷史趨勢(shì),對(duì)全局設(shè)置限制。比如前30天每天驗(yàn)證碼短信量總量都在30萬(wàn)上下浮動(dòng),那我們可以設(shè)置每天的短信調(diào)用上限為40萬(wàn),超出則進(jìn)行限制、告警。
示例代碼:
day_limit?=?get_from_config(day_limit)??
phone_limit?=?get_from_config(phone_limit)??
??
??
if?not?validate_captcha():??
????return?error('驗(yàn)證碼錯(cuò)誤')??
??
#?發(fā)送數(shù)量可以存在redis??
if?total_send_count()?>?day_limit:??
???#?do_something?告警??
????return?error('短信接口已經(jīng)達(dá)上限')??
??
if?phone_send_count()?>?phone_limit:??
????return?error('你的短信發(fā)送已達(dá)上限,請(qǐng)稍后再試')??
??
send_message()??
上面這種上限的方式一定程度上可以在被攻擊的情況下及時(shí)止損,但是也有小概率誤殺或者局部影響整體的情況出現(xiàn),所以需要看實(shí)際影響使用。
比如前幾天趨勢(shì)都是正常的,但是某天進(jìn)行促銷或活動(dòng),又或者是任何突發(fā)的流量進(jìn)來(lái),這時(shí)候這種全局上限的方式會(huì)影響正常用戶的使用。
再比如說(shuō),用戶當(dāng)天可能由于各種原因,一段時(shí)間內(nèi)某個(gè)操作頻繁的獲取驗(yàn)證碼,導(dǎo)致短信驗(yàn)證達(dá)到上限,會(huì)影響到他所有短信接口都無(wú)法使用。
一個(gè)蘿卜一個(gè)坑
您的短信內(nèi)容未備案
上面我們是根據(jù)用戶和全局做管控,實(shí)際上我們還可以根據(jù)短信的使用場(chǎng)景,通過(guò)短信模板來(lái)做管控。
對(duì)接過(guò)三方短信供應(yīng)商的都知道,絕大部分三方供應(yīng)端都是需要提供短信模板備案,才可以正常發(fā)送的。我們服務(wù)本身也可以,或者說(shuō)也需要使用模板做管控。
根據(jù)模板來(lái)做發(fā)送量的限制
我們可以根據(jù)不同模板的發(fā)送量趨勢(shì)來(lái)做對(duì)應(yīng)的短信告警
某些場(chǎng)景下,不同模板的短信可以配置不同的供應(yīng)商
示例代碼:
def?send_message(template_code,?params?=?{}):??
"""??
發(fā)送短信不再是直接發(fā)送短信內(nèi)容,而是通過(guò)模板來(lái)發(fā)送???
:param template_code 注冊(cè)過(guò)的短信模板編碼,比如 login_code,可以根據(jù)code查詢到對(duì)應(yīng)的模板內(nèi)容:??
?您好,您的驗(yàn)證碼為:${code},xxxxxxx??
??????
:param 模板變量,比如:?{code: 123456}??
"""??
?#?模板內(nèi)容和一些其它參數(shù)??
?template?=?get_template_by_code(template_code)??
??????
?#?驗(yàn)證模板是否存在??
?if?not?template:??
?????return?error()??
??????
????#?是否已達(dá)上限,全局或指定手機(jī)號(hào)??
????if?template_limit(template_code,?phone):??
?????return?error()??
??????????
????#?發(fā)送短信??
????do_send_message(phone,?template)??
??????
????log_template(phone,?template)??
??
#?注冊(cè)短信模板,一般是在管理后臺(tái)提供,需要人工審核??
def?register_template():??
???pass??
增加短信模板更多的是讓我們可以做到更全、更細(xì)粒度的管控,同時(shí)可以為后續(xù)的分析或風(fēng)控提供數(shù)據(jù)依據(jù)。
實(shí)名制
請(qǐng)先進(jìn)行實(shí)名認(rèn)證后再進(jìn)行該操作
我們可能會(huì)有一些非驗(yàn)證碼類型的短信是可以被用戶觸發(fā)的,有可能是直接觸發(fā),也有可能是間接觸發(fā)。
舉個(gè)栗子:比如某個(gè)產(chǎn)品或活動(dòng)新增了一個(gè)邀請(qǐng)功能,用戶在登錄后,通過(guò)某個(gè)按鈕或功能,邀請(qǐng)用戶注冊(cè)或回歸之類的行為。
這個(gè)時(shí)候,用戶就可以間接的觸發(fā)短信,比如:${nickName}邀請(qǐng)您注冊(cè)/回歸XXX產(chǎn)品。那么這種接口很可能被攻擊者利用,比如把nickName修改為攻擊者想要發(fā)送的內(nèi)容。
這種情況下我們首先肯定是在活動(dòng)的設(shè)計(jì)上就需要評(píng)估風(fēng)險(xiǎn)和有對(duì)應(yīng)的預(yù)防措施,同時(shí)在短信服務(wù)這塊怎么防御呢?
我們可以針對(duì)這些可能存在高風(fēng)險(xiǎn)的短信模板做特殊的處理,比如需要發(fā)送該模板內(nèi)容,需要用戶一定進(jìn)行了實(shí)名認(rèn)證之后才可以操作。
示例代碼:
#?模板內(nèi)容和一些其它參數(shù)??
template?=?get_template_by_code(template_code)??
??
#?其它校驗(yàn)略??
validate(phone,?template)??
??
risk_level?=?template.risk_level??
??
??
#?這里大家可以想想這種場(chǎng)景下可以使用什么設(shè)計(jì)模式~~??
if?risk_level?>?60:??
????if?not?certification(phone):??
????????return?error()??
??
if?risk_leve?>?30:??
????#?do?some_thing??
????pass??
...??
??
send_message(phone,?template)??
關(guān)于內(nèi)容這塊,理論上所有用戶輸入并且可以對(duì)外展示的內(nèi)容,都是需要做內(nèi)容檢測(cè)的。這塊這里不展開,大家可以自行了解,或者關(guān)注公眾號(hào)Java技術(shù)棧搜索閱讀更多。
風(fēng)控?風(fēng)控!
檢測(cè)到您本次操作存在風(fēng)險(xiǎn),操作被拒絕
當(dāng)我們的業(yè)務(wù)越來(lái)越大,并且面向的用戶越來(lái)越復(fù)雜的時(shí)候,上面我們提到的這些簡(jiǎn)單的規(guī)則很難應(yīng)付業(yè)務(wù)或用戶的復(fù)雜多變。
這時(shí)候就需要通過(guò)數(shù)據(jù)分析的方式,來(lái)動(dòng)態(tài)的、實(shí)時(shí)的調(diào)整我們的規(guī)則和處理方式,以及提供風(fēng)險(xiǎn)分析、預(yù)測(cè)等功能。這時(shí)候我們可能需要有一個(gè)獨(dú)立的風(fēng)控服務(wù)。
做過(guò)銀行業(yè)務(wù)的小伙伴可能會(huì)接觸得比較多(我自己沒(méi)有做過(guò)銀行系統(tǒng)),可以給點(diǎn)經(jīng)驗(yàn)和建議 - -!
那我們看到上面有看到,針對(duì)不同的模板的場(chǎng)景來(lái)確定風(fēng)險(xiǎn)等級(jí),然后來(lái)做不同的操作,這塊其實(shí)就涉及到風(fēng)控相關(guān)了。只是比較初級(jí),比如風(fēng)險(xiǎn)等級(jí)如何確定?每個(gè)風(fēng)險(xiǎn)等級(jí)需要做什么樣的事情?如何進(jìn)行動(dòng)態(tài)的配置等等。
舉個(gè)栗子:
我們可以收集用戶的行為軌跡(注冊(cè)時(shí)間、登錄次數(shù)、頁(yè)面訪問(wèn)情況等)來(lái)分析一個(gè)用戶,確定用戶的風(fēng)險(xiǎn)等級(jí),再?zèng)Q定他可以發(fā)送哪些短信
根據(jù)模板的歷史趨勢(shì),來(lái)自動(dòng)判斷相應(yīng)短信模板的合理范圍,如果達(dá)到上限,則認(rèn)為存在風(fēng)險(xiǎn)操作,可以做對(duì)應(yīng)的處理
配置相應(yīng)的規(guī)則,如果某個(gè)模板的短信內(nèi)容(和模板的區(qū)別是,變量一直沒(méi)有變化)重復(fù)N次則認(rèn)為存在風(fēng)險(xiǎn)
等等
風(fēng)控不僅僅適用于短信接口的風(fēng)險(xiǎn)識(shí)別,還包括注冊(cè)、登錄、支付操作等等。這個(gè)也不是一蹴而就的,需要長(zhǎng)時(shí)間的積累和建設(shè)。
比如上面說(shuō)到的用戶行為軌跡和模板趨勢(shì),都需要有全面的埋點(diǎn)和數(shù)據(jù)平臺(tái)作為支撐。還有如果業(yè)務(wù)要求比較高,還需要開發(fā)適合自己業(yè)務(wù)的規(guī)則引擎。但是當(dāng)風(fēng)控系統(tǒng)建設(shè)起來(lái)之后,效果也是明顯的!
對(duì)于未登錄的用戶,實(shí)際上也可以在用戶訪問(wèn)頁(yè)面時(shí)通過(guò)一系列的屬性生成一個(gè)唯一token,用于標(biāo)識(shí)這個(gè)用戶。這樣即使用戶未登錄,也會(huì)處于風(fēng)控服務(wù)的監(jiān)控下。關(guān)于token的使用教程可以關(guān)注公眾號(hào)Java技術(shù)棧搜索閱讀更多。
結(jié)論
上面我們簡(jiǎn)單說(shuō)了一下如何防止短信接口被刷,這一塊的安全不僅涉及到金錢(我見過(guò)短短10分鐘被刷幾萬(wàn)塊、幾十萬(wàn)的都有),也會(huì)影響到我們產(chǎn)品/品牌的聲譽(yù)。
想象一個(gè)用戶收到了一批垃圾短信,但是短信簽名帶的都是我們的簽名,這對(duì)公司的品牌影響有多大!
希望這篇文章可以幫助到大家,愿天下沒(méi)有被盜刷的短信!





關(guān)注Java技術(shù)棧看更多干貨


