『身份驗證機制遭破解』聽起來很技術,但『盜帳號』大家應該就都很有感了。沒錯,盜帳號不只是朋友之間惡搞的玩笑,而是真實世界駭客入侵手法最大宗沒有之一。那麼,被盜帳號是誰的錯呢?
先想想你都怎麼盜朋友帳號的吧。
有可能是趁他去上廁所的時候偷發文,也有可能是他用學校電腦卻忘了登出,更有可能的是『啊他就萬年一組帳號啊,不就是名字加生日嘛』。
沒錯,使用弱密碼的確是帳號被破解最大的原因之一。弱密碼不只是 qwerty monkey iloveyou 這種隨便一個字典檔就可以在半小時內爆開的常見字串,還包含跟個資相關,用名字、生日、電話、親朋好友的資訊組合而成的字串。就算你在前後多加一個驚嘆號或數字,對駭客來說也只是多花一分鐘就能破解的。而一旦破獲一組帳密,你的多個帳號大概也跟著淪陷了,畢竟,大多數人都是一兩組密碼打天下而且從來不更換的,就算會換也通常有某種規律,例如從 iamhappy1 變 iamhappy12 再到 iamhappy123。
你可能覺得,駭客哪會這麼了解我?的確,駭客可能不『認識』你,但別忘了,你在社群軟體上的貼文留言、你的朋友清單、你在別的網站或問券上無意留下的資料,全都是駭客秘密搜集的拼圖碎片!有了這麼多數位足跡,要知道你的出生年月日、國小國中高中學號、常去的店跟要好的朋友,可是易如反掌呢!
但如果不讓你用這些跟你相關的資訊,要求你每個登入頁面都要用完全不同的 10 位亂數當密碼,你記得住嗎?
根據密碼管理軟體 NordPass 統計,一個人平均有 100 組密碼。也就是說,在不使用密碼管理軟體的情況下,你必須腦袋裡牢牢記住 100 個毫不相干的 10 位亂數。而就算你使用了密碼管理軟體來儲存這 100 個亂數,如果你的 master password 本身還是弱密碼,那駭客只要破解它就可以獲得你全部的密碼了。
可見對大多數人來說,完全不使用弱密碼顯然不是可行的做法。那我們還能從哪裡防止『盜帳號』呢?
回想前面的情境,會發生『被盜帳號』通常是因為使用了能被預測跟列舉嘗試的密碼,既然難以杜絕『被預測』的可能,我們至少可以往『被列舉嘗試』這邊防堵。
如果攻擊者沒辦法透過登入頁面的反應來知道攻擊是否成功,或是只有少少的幾次機會可以嘗試,那只要密碼不是最直覺最好猜的那幾組就能暫時躲過一劫。
注意,這裡說的暫時是指駭客沒辦法非常快速輕鬆的破解你的密碼,但如果你在多個服務都重複使用同一組密碼,那只要有夠多網站就會累積夠多機會讓駭客多花一點時間撞到正確的那一組!
作為開發者,該如何防範『帳號密碼被列舉嘗試』呢?讓我們看看一個經典範例。
# 開發者與駭客的角力
小明是個新手工程師,學成後興沖沖加入一個團隊開發電商平台,負責管理客戶登入跟設定的模組。不過半年,電商平台大獲好評,使用者每個月都以倍數成長,小明覺得錢途光明,非常有成就感。結果兩天後在新聞上看到:
驚!知名電商平台洩漏大量個資!
傳出知名電商平台遭駭,竟有高達 300000 筆客戶資訊於暗網拋售,內容包含信用卡、消費紀錄等資訊,受害範圍遍佈歐洲亞洲等數十國家,其中不乏知名廠商。據相關人士於 twitter 透露,由於平台登入頁面存在弱點,駭客以暴力破解獲取多組帳密資訊,包含某管理員權限帳號,進而得以竊取大量個資。
小明馬上找來長期接觸資安的好友阿柚諮詢,阿柚一看先問實作邏輯。小明非常困惑,不就是把使用者輸入的帳號拿去查一下資料庫,如果有這個使用者再去查一次密碼嗎?如果查無此使用者,畫面上就會顯示『帳號錯誤』,密碼不對就會顯示『密碼錯誤』,教學文章不都是這樣做的嗎?
阿柚直搖頭,列出三大罪狀:
- 先查帳號再查密碼
- 錯誤訊息太明確
- 沒有任何登入次數限制
看著小明一臉問號,阿柚娓娓道來:
# 先查帳號再查密碼
先查帳號再查密碼看似合理,畢竟這樣才能跟用戶說到底是哪一個打錯了嘛!但這個做法其實也透露給駭客:一個帳號存不存在,跟查幾次資料庫有關。而大家應該都知道查詢資料庫其實是很花時間的,所以在假設其他程式邏輯都相同的情況下,查兩次資料庫跟查一次應該有回覆速度上明顯的差異。
因此,如果我拿一百個帳號去嘗試登入,結果發現其中一個頁面回覆的的時間是其他的兩倍,那我就可以推論,這個帳號既然用了兩次資料庫查詢,那他應該是存在的吧?
利用這個瑕疵,駭客就可以拿一個很大的帳號名單逐一嘗試登入,然後透過回覆速度判斷資料庫裡面存在哪些帳號,再利用過濾完的帳號名單第二次暴力破解使用弱密碼的用戶。不出意外,小明用的後台管理帳號名稱就是最直觀的 admin,當然一下就被字典檔發現啦!
# 錯誤訊息太明確
小明惱了,debug message 不清不楚成何體統,使用者怎麼知道哪裡打錯?沒錯,當你把錯誤訊息寫得越精確,你輕鬆、使用者輕鬆、駭客更輕鬆。他甚至也不用管實作邏輯是不是會有回應速度上的差異,只要單純根據錯誤訊息就知道到底是帳號錯還是密碼錯,輕輕鬆鬆列舉所有的帳號!
從資安的觀點來講,錯誤訊息應該盡可能籠統,例如任何登入錯誤都用『帳號密碼錯誤』或『您輸入的資訊有誤』帶過,讓駭客更難判斷攻擊是否成功。
# 沒有任何登入限制
最大的問題,就是沒有針對『嘗試登入』這個舉動做任何限制!因為任何人都可以輕易地對網站做無限多次的登入嘗試,概念上就像買樂透一樣,你把每一種可能的組合都包牌買下來總是會中大獎的吧?那為什麼我們還要努力工作,不能走進彩券行大手一揮全包下來?因為包牌的成本遠超過中獎的獎金,這個行為的期望值太低了,不值得我們這樣做。
同理,對暴力破解來說,最根本的解決方法就是增加駭客的成本,讓他要花的時間跟資源大到暴力破解變得不可行,就能有效抵擋這種攻擊。
聽了阿柚的解說,小明打開 log 一看,發現從一個 IP 12.34.56.78 發了將近百萬個登入的 request,瞬間恍然大悟『那我只要限制每個 IP 如果連續五次請求都打錯密碼就鎖帳號,這樣就可以了吧!』。
阿柚想了想,再搖搖頭,這個做法還是有邏輯錯誤。
# 登入邏輯錯誤
因為鎖帳號的邏輯是『連續五次錯誤』,所以駭客可以先註冊一組帳號,然後每試四次就成功登入自己的帳號一次,打破『連續』的條件,這樣 4+1 的方法不斷重複一樣可以繞過小明的限制達到暴力破解。再者,如果是在帳號密碼分開查詢資料庫的實作下,駭客還可以用 4+1 搭配前面提過的時間判斷法來列舉平台上的帳號。因此以來源 IP 為判斷對象鎖帳號的邏輯其實無法阻止駭客的攻擊。
而且眼尖的阿柚還發現,登入用的 POST 請求居然允許使用 JSON 格式的 post data:
{
"username": "admin",
"password": "password"
}
這樣根本不用發大量請求,只要把大量的密碼們一次用 array 的方式送過去,如果裡面有一組正確的就可以直接登入啦!
{
"username": "admin",
"password": [
"password",
"admin",
"pass123",
"iloveyou",
"letmein"
...
]
}
小明沉思許久,要考慮的事情真多呀!不只要看登入格式,還要限制每個帳戶登入失敗幾次就要鎖住,而且錯誤訊息跟資料庫查詢方式都要調整,真是大工程。
不過既然頭都洗下去了,不如再給他做個最近正流行的 2FA 吧!
# 2FA
2FA (2 factor authentication) 翻譯為雙因素驗證,也就是輸入正確的帳號密碼後還必須透過 email 或簡訊的方式再驗證一次,都過了才算登入成功。廣義來說為一種 MFA (multiple factor authentication),或多因素驗證,為了防止單一驗證方法被破解而延伸的多層防線戰術。
要做當然好,阿柚說,但實作上也是要多注意呀!
先前新聞上( Hackers could bypass cPanel 2FA in minutes using brute-force attacks )才報導軟體因為 2FA 驗證碼沒有限制提交的時間跟次數,導致駭客可以在幾分鐘內暴力破解僅 4 個數字組成的驗證碼。
要做好 2FA 必須加上一些限制,例如驗證碼有效的時間不應過長、驗證碼應隨機無法預測、要求輸入錯誤三次就要重新傳送驗證碼等等。
另外,輸入驗證碼後的 API 也應做好保護。例如曾有網站在驗證碼比對成功後對後端發送 /auth/2fa?verify=true
來表示驗證成功,或甚至直接跳轉 /myaccount/
進到登入後頁面,這時 API 邏輯根本與 2FA 無關,駭客根本可以忽略驗證碼,直接發同樣的 API 就可以登入啦!
或是不久前 Researchers trick Duo 2FA into sending authentication request to attacker-controlled device 報導了 2FA 驗證時的 state 與當前嘗試登入的 session 無關,導致駭客可以用自己登入時 2FA 驗證的 state 取代正在嘗試破解的 2FA 驗證,導致系統誤以為受害者的裝置已接受驗證。
本來的機制是:輸入帳號密碼後會觸發 2FA 驗證流程,首先會發送下圖中第一個請求 POST /frame/prompt
,其中 sid
代表的是當前的 session id。這時伺服器會回傳 txid
,讓網頁端可以持續查詢(poll) 2FA 驗證的狀態,例如是否已傳送通知、使用者驗證了沒等資訊,來判斷登入是否成功。在網頁首次請求 POST /frame/status
時,伺服器才會對使用者的手機發送推播通知,引導使用者進行第二次驗證。直到手機端點擊確認,伺服器更改了驗證狀態,網頁端才會在下一次查詢時得到 txid
狀態為 allow 的結果,完成驗證。
那駭客是如何繞過的呢?
問題出在辨識身份的 sid
跟辨識 2FA 驗證狀態的 txid
這兩個並沒有關聯。也就是說,我今天只要拿一個已經驗證成功的 txid
搭配任意我想要認證的 sid
,系統根本分不出來這個狀態是不是屬於這個身份,就像畢業證書上如果沒有寫名字,那任何人都可以拿這張紙說自己畢業了。
如下圖,駭客只要自己擁有一個帳號並且知道受害者的帳號密碼,就可以用自己驗證成功的 txid
搭配用受害者帳密嘗試登入的 sid
來騙過系統完成驗證。你可以在這裡找到更詳細的 writeup。
# 那我該怎麼防?
小明聽了這麼多早已暈頭轉向,到底該做到哪些機制或遵守哪些準則呢?
小明跟阿柚經過一番討論,列出了一系列注意事項:
- 確保登入資料必須經由 HTTPS 加密通道傳送
- 不要在驗證完成前的 response 中透露登入者的 username 或是 email 等資訊
- 防止駭客自動化暴力破解:
- 登入失敗的錯誤訊息應該盡可能籠統而單一,不要過於詳細
- 防止駭客透過回覆速度或是頁面中的資訊判別某個帳號是否存在系統中
- 應限制帳戶可嘗試登入的次數,例如鎖帳戶或是需等待一段時間才能嘗試
- 同時可以做 IP-based rate limiting 限制請求發送速率
- 加入其他防自動化的機制,如 CAPTCHA
- 確保程式邏輯沒有漏洞(包含登入、密碼重置、多因素驗證等等)
- 幫助使用者設定強一些的密碼,例如 JavaScript 函式庫 zxcvbn 會在使用者建立帳號時進行檢查,禁止使用者選擇強度不足的密碼。雖然不代表密碼一定安全,但至少會符合一定強度的密碼規則,例如:至少八個字元、需使用大小寫與特殊符號、不能跟帳號類似等等。
# 結語
看似簡單又常見的登入功能,其實細節中藏著非常多的魔鬼。尤其大多數比較敏感的動作跟資訊都要先經過登入才能存取,如果連這個守門人都不可靠,那可說是門戶大開呀!進行身份驗證時,務必多檢查是否能抵檔駭客的暴力破解以及是否存在邏輯上的缺失,越關鍵的功能越是需要多層防護。
被盜帳號是誰的錯呢?想想你都用哪些密碼跟服務吧,要是你註冊任何服務都用同樣幾組密碼,又使用一些安全等級堪憂的網站,可要小心囉!
Tag
Recommendation
Discussion(login required)