引子前端
上聯:這個需求很簡單後端
下聯:怎麼實現我無論安全
橫批:今晚上線服務器
早上開完站會,小李領了張新卡,要對登陸功能作升級改造,在原來只支持用戶名密碼登陸模式的基礎上,新增手機號和短信驗證碼登陸。微信
業務分析師薇薇早就準備好了故事卡,而且也考慮到這個功能的特殊性,除了日常的業務性驗收標準外,還專門添加了一些和安全有關的條目。這張故事卡看上去是這樣的:多線程
故事卡-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分鐘有效期的設置。
短信驗證碼登陸的功能上線後,運行狀態一直比較平穩,然而這種平靜的氛圍被一通電話打破了。
「喂,對,是我」,老羅桌上的電話響了,他忙着寫代碼,歪着脖子用肩膀和臉夾住話筒說道:「是客服部啊,有什麼事我能夠幫忙的?」
「是這樣,咱們今天忽然收到不少顧客打來的電話,抱怨說收不到短信驗證碼,登陸不了帳戶,他們基本都是新用戶,只有用手機註冊的帳號,沒有用戶名密碼,因此也不能用原先的用戶名密碼去登陸帳號。咱們只好讓顧客再等會兒試試,多是信號很差,但後來他們反饋說仍是收不到咱們的短信,並且只是收不到咱們的短信,因此,大家那邊能幫忙看看是怎麼回事嗎?」電話那邊一口氣講了一堆話。
「還有這種事,行,我知道了,咱們立刻調查分析一下。」老羅剛掛斷電話,運維部的同事過來找到老羅,說短信配額今天消耗得很厲害,已經觸發了2次告警了,運維同事作了一下簡單的分析,發現早上10點和下午2點左右有兩批次大量發送登陸短信驗證碼的請求,但又沒有觀察到對應的後續登陸請求,判斷多是被黑客攻擊了,因而臨時性的屏蔽了攻擊來源IP地址的訪問。
「來找你就是想和開發團隊共同調查下這個問題,看接下來怎麼處理會比較好。」運維部的同事說道。
老羅以爲這個事和剛剛接到的客服部門說的是同一件事,便把剛纔電話裏聽到的信息和運維同事講了一遍。
「這更能證明是黑客攻擊了,並且看來他們的目標應該不是暴力登陸,而是故意消耗短信發送配額,一旦配額被用完,用戶就沒法正常登陸,也算是某種程度上的拒絕式服務攻擊了。」 運維部的同事說完看向老羅。
老羅如有所思的說道:「沒想到他們還能這麼玩兒。咱們目前只限制了一個手機號60秒內發一次驗證碼,卻沒有應對大量不一樣手機號的狀況。」
「那如今怎麼處理比較好呢?雖然臨時禁用了攻擊者的IP,但咱們擔憂會誤傷真實用戶,並且黑客也可能會變換IP來繼續進行攻擊。」運維同事繼續問道。
「有辦法,在發短信驗證碼以前先要求輸入圖形驗證碼。」
「嗯,有道理,大家何時能作好上線?」
「我如今就加」,老羅還沒說完就已經開始寫代碼了:「一下子弄完緊急上線。」
「行,我回去安排一下,我們運維部全力配合。」
「看來以前那張故事卡里的安全驗收標準還差了一條」,老羅天然自語道:「若是加上一條圖形驗證碼的要求恐怕就不會出這個事兒了。」
發送短信驗證碼以前,先驗證圖形驗證碼是否正確
「喂喂喂,這搞的什麼鬼?」用戶體驗設計師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洞見