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