不就是個短信登錄API嘛,有這么復雜嗎?

f8f814630cfc 2019-06-27 17:26:45 1438

引子
上聯:這個需求很簡單
下聯:怎么實現我不管
橫批:今晚上線

Part 1:暴力破解

早上開完站會,小李領了張新卡,要對登錄功能做升級改造,在原來只支持用戶名密碼登錄模式的基礎上,新增手機號和短信驗證碼登錄。

業務分析師薇薇早就準備好了故事卡,并且也考慮到這個功能的特殊性,除了平常的業務性驗收標準外,還專門添加了一些和安全有關的條目。這張故事卡看上去是這樣的:

故事卡-274
作為用戶,我可以通過手機號和短信驗證碼登錄,以便于我更方便的登錄。
安全驗收標準:

  • 短信驗證碼有效期5分鐘

  • 驗證碼為4位純數字

  • 每個手機號60秒內只能發送一次短信驗證碼

小李看到故事卡中提到,驗證碼長度只有4位而且還是純數字,隱約覺得強度有些不夠,擔心萬一黑客來個多線程并發請求,或者拿一個集群來暴力登錄,有可能會趕在有效期內破解出合法的驗證碼。

小李把自己的擔心講給了業務分析師薇薇,并且建議把驗證碼長度增加到6位,或者在保持4位長度的情況下,改為數字和字母的組合,目的是增加驗證碼復雜性,提高暴力破解的門檻。

薇薇聽了這兩種選擇后直搖頭,說道:“我理解你的擔心,可是業務部門那邊的需求很明確,就是為了優化用戶登錄體驗,所以才決定做手機號和驗證碼登錄,如果把驗證碼弄的這么復雜,那用戶體驗也好不到哪里去,不符合這個故事卡的初衷啊。”

“對于用戶而言,4位數字驗證碼確實好記好填,可是對于黑客而言,就能很容易的完成暴力枚舉,理論上最多1萬次請求就能遍歷完所有的驗證碼,更何況黑客沒那么倒霉,要嘗試到第1萬次才猜對……”,小李說道。

為了滿足用戶體驗而在安全性上做出妥協,這種事情小李覺得自己無法說服自己,正準備掏出紙和筆跟薇薇做詳細解釋黑客攻擊手段的時候,團隊技術負責人老羅聽見了他們兩的討論,慢慢脫下帽子,摸了摸正在朝著“地中海”模式演進的烏黑的秀發,說道:“那啥,服務器在驗證登錄請求的時候,不管驗證碼匹配還是不匹配,存在Redis里的驗證碼只要被取出來就立即作廢,根本不給黑客暴力破解的機會。”

小李的團隊已經搭建好了Redis,用來存儲登錄過程中發給用戶的短信驗證碼,是一個手機號和驗證碼的鍵值對。

“對啊”,小李感覺眼前一亮,說道,“服務器在比對請求中的驗證碼和Redis中保存的這個用戶手機號所對應的驗證碼的時候,如果發現不匹配,那依然還是直接把Redis中的這個驗證碼作廢。這樣黑客發第二次登錄請求的時候,會因為Redis中找不到對應的記錄而登錄失敗。這樣既避免了暴力枚舉攻擊,同時也不再需要增加驗證碼的強度,導致用戶體驗的下降了。”

小李建議把剛才的討論結果寫到故事卡里,而薇薇提議能否不要立即作廢:“萬一用戶輸入驗證碼的時候手滑輸錯了,豈不是要等幾十秒的時間再重新發第二個驗證碼?”

“可以做到驗證碼3次輸入錯誤后就作廢嗎?”薇薇問到。

“可以的,這個不難”小李堅定的回答到。

“好,那我們加一條安全驗收標準吧”,薇薇邊說邊修改了故事卡,新增加了一條:

保存于服務器端的驗證碼,至多可被使用3次(無論和請求中的驗證碼是否匹配),隨后立即作廢,以防止暴力攻擊

“對了小李”,老羅喝了口咖啡,最近連續的加班讓老羅感覺很疲憊,只能靠喝咖啡強打精神。“60秒內只能發1次短信那條,別忘了前后端都要做檢查。”

“知道知道,前端做不做都無所謂,關鍵是在后端要做限制。”小李連連點頭。

“好,那就這么做,去忙吧”。老羅轉身坐下,正準備繼續剛才被打斷的工作,此時一個念頭快速在腦海里一閃而過,老羅在電腦上打開短信登錄的這張故事卡,從頭到尾又看了一次,最后目光停留在“短信驗證碼有效期5分鐘”這條驗收標準那里。

“短信每60秒發一次”,老羅心想:“但有效期是5分鐘,那第61秒的時候假如又請求發送一次驗證碼,這時第一次發送的驗證碼還沒過期,服務器端該怎么處理這個請求會比較穩妥呢?

顯然,第二個驗證碼直接覆蓋掉第一個會更加安全,也就是至始至終都只有一個處于有效狀態的驗證碼,但這會不會給用戶帶來困惑?畢竟偶爾還是有手機信號不好,等了1分鐘多鐘之后才收到第一個驗證碼的情況。

如果不替換而是追加驗證碼呢?最極端的情況是會出現一個手機號有5個有效驗證碼的情況,會增加黑客暴力破解的成功概率。不過因為一個驗證碼最多只能被使用3次,之后就被作廢了,所以實際上黑客暴力破解的難度依然很高。

總的來說,直接覆蓋的做法用戶體驗不佳但更安全,依然有效的做法用戶體驗更好但相對而言安全性略有降低。”

經過反復思考后,老羅最終選擇保留驗證碼5分鐘有效期的設置。

Part 2:防不勝防

短信驗證碼登錄的功能上線后,運行狀態一直比較平穩,然而這種平靜的氛圍被一通電話打破了。

“喂,對,是我”,老羅桌上的電話響了,他忙著寫代碼,歪著脖子用肩膀和臉夾住話筒說道:“是客服部啊,有什么事我可以幫忙的?”

“是這樣,我們今天突然收到很多顧客打來的電話,抱怨說收不到短信驗證碼,登錄不了賬戶,他們基本都是新用戶,只有用手機注冊的賬號,沒有用戶名密碼,所以也不能用原先的用戶名密碼去登錄賬號。我們只好讓顧客再等會兒試試,可能是信號不好,但后來他們反饋說還是收不到我們的短信,而且只是收不到我們的短信,所以,你們那邊能幫忙看看是怎么回事嗎?”電話那邊一口氣講了一堆話。

“還有這種事,行,我知道了,我們馬上調查分析一下。”老羅剛掛斷電話,運維部的同事過來找到老羅,說短信配額今天消耗得很厲害,已經觸發了2次告警了,運維同事做了一下簡單的分析,發現早上10點和下午2點左右有兩批次大量發送登錄短信驗證碼的請求,但又沒有觀察到對應的后續登錄請求,判斷可能是被黑客攻擊了,于是臨時性的屏蔽了攻擊來源IP地址的訪問。

“來找你就是想和開發團隊共同調查下這個問題,看接下來怎么處理會比較好。”運維部的同事說道。

老羅覺得這個事和剛剛接到的客服部門說的是同一件事,便把剛才電話里聽到的信息和運維同事講了一遍。

“這更能證實是黑客攻擊了,而且看來他們的目標應該不是暴力登錄,而是故意消耗短信發送配額,一旦配額被用完,用戶就無法正常登錄,也算是某種程度上的拒絕式服務攻擊了。” 運維部的同事說完看向老羅。

老羅若有所思的說道:“沒想到他們還能這么玩兒。我們目前只限制了一個手機號60秒內發一次驗證碼,卻沒有應對大量不同手機號的情況。”

“那現在怎么處理比較好呢?雖然臨時禁用了攻擊者的IP,但我們擔心會誤傷真實用戶,而且黑客也可能會變換IP來繼續進行攻擊。”運維同事繼續問道。

“有辦法,在發短信驗證碼之前先要求輸入圖形驗證碼。”

“嗯,有道理,你們什么時候能做好上線?”

“我現在就加”,老羅還沒說完就已經開始寫代碼了:“一會兒弄完緊急上線。”

“行,我回去安排一下,咱們運維部全力配合。”

“看來之前那張故事卡里的安全驗收標準還差了一條”,老羅自然自語道:“如果加上一條圖形驗證碼的要求恐怕就不會出這個事兒了。”

發送短信驗證碼之前,先驗證圖形驗證碼是否正確

Part 3:權衡

“喂喂喂,這搞的什么鬼?”用戶體驗設計師Jenny抓住路過的老羅說:“我不過就是休了兩天假,回來之后怎么發現登錄這里多了個圖形驗證碼出來?”

老羅向Jenny解釋了這個圖形驗證碼的由來,是出于安全的考慮才增加的。

“我知道安全很重要,可是這圖形驗證碼太傷害用戶體驗了,現在顧客登錄過程中就要再多做一次輸入,如果填錯了還得重新再來一次,而且這圖形驗證碼的風格和我們的App風格明顯不匹配,另外,這圖形驗證碼是不是也太扭曲了,我都看錯好幾回了……。”Jenny顯然并不認同這個方案。

“風格我們可以修改,這不是還有你嘛。”老羅為難的說到:“難度高是因為現在的圖像識別技術突飛猛進,簡單圖片驗證碼很容易被破解。”

“莫非就沒有別的解決辦法了嗎?”Jenny繼續問道。

“其實也有,就看公司舍不舍得花這筆錢了。”老羅接著說:“登錄界面可以動態決定是否要求輸入圖形驗證碼,對于正常用戶可以讓他們無需輸入圖形驗證碼,對于黑客或者疑似黑客的人,就要求他們輸入。”

“這聽上去很好啊,另外,這和舍不舍得花錢有什么關系?”Jenny不太明白。

“要動態決定是否要求輸入圖形驗證碼這件事兒,其實就是判斷當前用我們App的人是真實的顧客還是黑客。我們自己沒這個判斷能力,不過有提供這種服務的第三方API,只是他們都不是免費的,得花錢買。”老羅向Jenny解釋到。

阿某云和騰某云等等都提供這類服務,其主要原理是,服務器在處理登錄請求的時候,先盡可能多的收集該請求的上下文信息,例如登錄請求的來源IP地址,時間,手機號,User-Agent等等數據,并且把這些數據傳遞給第三方API,由他們進行一次分析判斷,并把結果返回給服務器,告訴服務器當前請求者是可信用戶還是可疑用戶。最終是否允許登錄成功的決定權還是在服務器這邊,只是借助了第三方API提供的分析結果來做判斷而已。

“我不懂技術,不過好像也聽懂了的樣子。"Jenny笑著說道。

“用第三方API做登錄判斷這事兒我拍不了板,得找領導批準,說不定還得走采購流程。”但老羅覺得這條路的方向是對的。

“走,我們去問問領導的意見,我實在受不了現在這個圖形驗證碼。”Jenny拉著老羅徑直朝著總經理辦公室走去。

尾聲

最終,老羅他們團隊用上了某云的第三方API做登錄防護,去掉了令Jenny抓狂的圖形驗證碼。經過和業務部門的商量,驗證碼有效期最后縮短到了2分鐘。

在這期間還出現了兩個小插曲。運維部門的同事偶然間發現,應用程序日志文件里居然保存了所有用戶的短信驗證碼,這是小李當初做調試的時候加上去的,后來忘記關掉了。好在并沒有造成泄露,后來團隊修復了這個問題。

另一個小插曲是,團隊做了微服務架構改造,把發送短信的功能拆分出來做成了一個獨立微服務,但卻沒有給這個新的接口設置好訪問控制權限,以至于任何人在無需登錄的情況下,只要向這個接口發起請求就能成功發送一條短信給任意手機,短信內容還可以自定義。這個問題在安全團隊做滲透測試的時候發現的,嚇得老羅渾身冒冷汗。所幸發現及時,做了緊急修復,并沒有造成安全事故。

薇薇后來把短信登錄的故事卡作為案例保存了起來,把安全驗收標準又重新做了一次梳理,所以最終的故事卡是這樣的:

故事卡-274
作為用戶,我可以通過手機號和短信驗證碼登錄,以便于我更方便的登錄。
安全驗收標準:

  • 短信驗證碼有效期2分鐘

  • 驗證碼為6位純數字

  • 每個手機號60秒內只能發送一次短信驗證碼,且這一規則的校驗必須在服務器端執行

  • 同一個手機號在同一時間內可以有多個有效的短信驗證碼

  • 保存于服務器端的驗證碼,至多可被使用3次(無論和請求中的驗證碼是否匹配),隨后立即作廢,以防止暴力攻擊

  • 短信驗證碼不可直接記錄到日志文件

  • 發送短信驗證碼之前,先驗證圖形驗證碼是否正確(可選)

  • 集成第三方API做登錄保護(可選)

沒成想,一個短信登錄API背后,還能牽扯出這么多事兒來。


文/ThoughtWorks馬偉

更多精彩洞見,請關注微信公眾號:ThoughtWorks洞見

广东26选5开奖结果查