1. 程式人生 > >我就是要用MD5!不用不行!那麼,怎麼防止被拖庫後洩露使用者密碼?

我就是要用MD5!不用不行!那麼,怎麼防止被拖庫後洩露使用者密碼?

寫在開頭
最近(2015年10月20日前後)某郵箱系統被曝“洩露”了上億使用者資料,包括密碼、密保問題等。無論是確有安全漏洞導致被“拖庫”,還是有心人利用“撞庫”的方法獲得的使用者資料,從留出的資料來看,使用者的密碼、密保等資訊都還是使用的MD5 Hash摘要進行儲存。


即便是已有組織早有警告MD5已經不再安全,仍然有浩若煙海的大小站點在使用MD5 Hash作為密碼資訊的儲存手段。隨手搜尋一下,我們就可以發現,網路上已有為數不少的站點,通過生成、索引字典資料庫的方法提供MD5等Hash摘要的原文反查服務。仔細研讀一下使用說明,就可以發現,不只是MD5,很多的MD5變體都能提供方便的反查。雖然部分記錄需要收費,批量查詢需要收費,但考慮到所查使用者資訊的潛在價值,其費用並不高。


接下來問題來了,使用MD5,怎麼防止被爆庫洩露使用者密碼?


接觸過密碼學的話,很容易就會想到一個法子:這簡單,不用簡單的MD5(Password)不就行了。
於是,出現了:
改用別的Hash, SHA1(Password),SHA256(Password)
多次巢狀,MD5(MD5(Password)),MD5(MD5(MD5(Password))),MD5(MD5(MD5(…(Password)…)))
花樣加鹽,MD5(Password+Salt),MD5(Salt+Password),MD5(MD5(Password)+Salt),MD5(MD5(Salt)+MD5(Password))


粗看之下,似乎都很有道理,我換了或是更先進或是用得較少的Hash函式,你做字典庫要麼效率變低開銷變大,要麼做得Hash種類冷門沒人查你不願做不願存;我多次巢狀,你跑同樣的字典,時間長了不只一點點;我花樣加鹽,鹽一隨機,你的字典還怎麼用?


但是仔細一想,做庫,做反查,有多大利益,就會有多大投入。畢竟當前硬體成本不斷在降低,硬體效能不斷在提升,而大中型站點、企業的資料庫洩露實際上也並不是什麼小概率事件。一份普通的使用者資料庫真的價值有限麼?關聯的庫一撞,或者拿到某人的萬用密碼,得到涉及資金的賬號,價值自不用多說,何況相關的黑產還有更加複雜的利用方法,在此也就按下不表了。


那麼,採用MD5的情況下,用什麼法子才能儘可能保護使用者的密碼,同時又能在不過多增加儲存、開銷的情況下保證安全的驗證呢?


以下在“花樣加鹽”的大方向上,提供一個改進方案:


使用者註冊,輸入:
使用者名稱
密碼


按照大多數情況,使用者名稱6~20字元,密碼8~32字元;我們假設使用者名稱、密碼都只限數字和字母大小寫。


(密碼在註冊/登入操作,提交至伺服器的時候,密碼通過RSA或其他非對稱加密進行加密,防止中間人截獲)


這樣,伺服器獲得了:最多20字元的使用者名稱username和最多32字元的密碼password(明文),


處理儲存時,先生成一串32字元的隨機字串stringfill(數字、字母大小寫),根據password的長度取出stringfill的若干字元補充在password之後,補充password至剛好32字元,記為password32,計算passhash=MD5(password32)
然後對該username條目實際儲存以下幾項:username, stringfill, passhash


舉個例子就是:
使用者填寫、提交註冊資訊,伺服器收到:
username=accenturejack8 (14字元)
password=aj88819810429  (13字元) [加密傳過來的,這裡是用伺服器私鑰解密後)


伺服器為使用者accenturejack8生成32字元的隨機stringfill=ViNmVArZloKTSJNBbV2ZYGtBTNWnrfsZ

然後取出前32-13=19字元,接在password後補全至32字元,即password被補全為:
aj88819810429ViNmVArZloKTSJNBbV2

然後取MD5(aj88819810429ViNmVArZloKTSJNBbV2)=8a69b0ef9f40a912d53fa62592ba9e48

實際儲存:
username=accenturejack8
Stringfill=ViNmVArZloKTSJNBbV2ZYGtBTNWnrfsZ
passhash=8a69b0ef9f40a912d53fa62592ba9e48


結合這個例子,可以發現,如果被爆庫,使用者accenturejack8的username, stringfill, passhash都被攻擊者獲得:
username=accenturejack8
stringfill=ViNmVArZloKTSJNBbV2ZYGtBTNWnrfsZ
passhash=8a69b0ef9f40a912d53fa62592ba9e48


攻擊者在拿到這些資訊以後,通過包括不限於目前的各類字典庫工具的幫助,也是絕無可能獲得accenturejack8的明文密碼或者進行關聯已知資料的撞庫攻擊的。首先,按照目前各字典庫公佈的庫大小資料,如某站“實時查詢記錄超過24萬億條,共佔用160T硬碟,成功率95%”,即便實際已經達到公佈資料的兩倍資料量,也決不可能有效覆蓋32字元(數字、字母大小寫)長度明文的摘要條目數(*)之萬一,更不必談反查的成功率了。


對於拿到庫後基於庫資料間的撞庫攻擊,由於隨機字串stringfill的存在,除非使用者使用的是恰好32字元的密碼,否則“撞中”的情形將只是小概率事件,且stringfill存的是完整的32位長,從資料表現並無法得知原密碼的長度;即便使用者恰好使用32位字元的密碼,那麼按前文所述,並無法用有效手段拿到明文,也就無從利用了。

最後需要注意的是:對於利用登陸入口的撞庫攻擊(比如:已從防範弱的站點拿到accenturejack8的明文密碼),以上的方法並不能避免使用者被非法人員登入賬號,僅能通過限制錯誤密碼登入次數,異常登入IP提醒,以及對應的鎖定登入等前端、後端功能上的保護方式儘可能減少使用者損失。不過話說回來,如標題所述,以上所述方案只能儘可能防止洩露密碼,如果已經洩露,那就並沒有辦法了。



(*: 這個條目數達到62^32,約等於2.2726579乘以10的57次方,“24萬億”=2.4乘以10的13次方)


【筆者拙見,歡迎捉蟲,歡迎討論,歡迎輕噴。】