驗證用戶密碼這件事

本篇經與 @Gnnng, @pollow 討論、查資料,在 MSTC 群中請教討論總結而成。

hash_salt
hash_salt

我們都知道,數據庫里不能儲存用戶密碼的明文,那怎樣存儲才是最科學的呢?

為什么不能存明文?

仔細想想,即使使用明文放在數據庫里,用戶還是看不到別人的密碼,那為什么不能使用明文存儲呢?這是因為你不能保證自己數據庫的安全。之前 CSDN 因為一個漏洞整站數據庫被拖下來,用戶的帳戶名和密碼就全部暴露在網站上了。這帶來一個更嚴重的問題,很多人在不同的站點上使用的都是同一個用戶名和密碼,別人拿著這個被暴露出來的密碼到其他各大網站,比如淘寶,支付寶,網銀等等試一下,用戶的損失就嚴重了。

加密 Hash

加密 Hash 是對數據的單向映射。單向映射就是指拿到 hash 后的值,無法得到原來的數據。上面那個問題,如果我們把用戶的密碼 hash 一下再放到數據庫里,然后每次用戶登入的時候我們拿到用戶的真實密碼都先 hash ,再與數據庫中的值進行比對,這樣即使數據庫被人拖下來,他也無法通過那個 hash 后的值得到用戶的真實密碼了。

Rainbow Table

可是真的是這樣嗎?一定程度上說是的,別人得到 hash 之后,的確幾乎不可能逆向算出原始密碼。但換個思路想一想,用戶的密碼通常是簡單并且容易構造的,只要把這些常見的密碼 hash 一下得到這個對應關系表,就很容易得到原始密碼了。這樣的對應關系表通常被稱為彩虹表[1](Rainbow Table)。

現在黑客們已經構造出 TB 級別的彩虹表[2],所以很多直接 hash 得來的密碼都會被直接查詢到。

Salt

那用什么辦法可以防止彩虹表的直接查詢呢,有人提出了一個解決方案——加鹽(salt)。具體來說就是得到用戶的真實密碼后,再隨機生成一個字符串(即鹽),把密碼和鹽用某種方式組合起來(即加鹽)然后再 hash 后放后數據庫中,同時把鹽也放入數據庫中。用戶登入的時候,從數據庫中取出對應的鹽,再和得到的用戶密碼組合一下進行 hash ,與數據庫中的 hash 值比對即可。

那為什么這樣就能避免彩虹表碰撞呢?如果數據庫被人拖下來,他獲得了 hash ,同時也獲得了鹽,如果碰撞成功,他應該很容易通過拿到的鹽得出用戶真實密碼啊?

是,也不是。

其實加鹽的真實目的,是對用戶的密碼進行強化。用戶常常會使用弱的密碼,比如幾位純數字之類,這太好構造,很容易碰撞出來。但是如果是幾十位,甚至幾百位的大小寫字母加數字還有特殊字符,這就很難構造了,因為這樣的組合數目實在太多了。我們可以看到在 Rainbow Crack 這個網站上面,1 - 9 位數字和字母的組合就達到了 13,759,005,997,841,642 種之多,數據量達到了 864 GB,在這個基礎上每增加一位或者增加一種字符帶來的都是指數級別的數據量增長。因此,只要我們的鹽足夠強,很難用彩虹表碰撞成功。而換一種策略,即使他使用拿到的每個鹽去構造新的一些彩虹表,代價也是巨大的。

多 hash 幾次?

有些人可能會想到,拿到用戶的密碼后,多 hash 幾次會不會從一定程度上避免彩虹表碰撞呢?答案是否定的。事實上,和大多數人想象得正好相反,這樣做反而更加不安全。

前面說,hash 函數是不可逆的,這是因為在 hash 的過程中丟掉了原始數據的部分信息。也就是說,hash 是減熵的。當進行多重 hash 的時候,整體的安全性其實取決于最弱的那個 hash 函數,因為如果單一 hash 碰撞,多重 hash 一定碰撞。

把上面那句話用表達式寫出來:

比如原先有函數 hash1,并且

數據庫里面存儲了 y1 作為校驗,原始密碼是 x1。如果有人碰撞出了 x2 ,在我們的數據庫中就驗證成功了。

而現在又有函數 hash2,并且

并且有

同時又有


如果用戶使用 x11 作為密碼,那么使用 x12, x21, x22 都可以得到相同的 z1,所以更加不安全了。

換種思路竊取密碼

如果我們按照上面的策略存儲密碼了,可以暫時認為數據庫方面是安全的了。如果要想竊取用戶的密碼,就應該從更薄弱的環節入手,比如網絡傳輸。現在仍然有大量的網站沒有使用 HTTPS 傳輸數據,這意味著用戶發送的數據可能在經過的每一個路由節點上被監聽到。所以還沒等服務器拿到用戶的密碼原文,中間人已經獲取到所有想要的信息了。

這時候怎么辦呢?最好的解決辦法就是換成 HTTPS,從根本上避免這種監聽。但如果做不到,我們可以退而求其次想一些折衷的辦法[3]

策略一,前端直接 hash 密碼送到后端進行加鹽 hash。不可行。因為如果有人從別處已經得到了一些 hash 后的值,那么他就不需要猜測用戶原來的密碼,直接把 hash 送到后端進行驗證就行了,反而降低了難度。

策略二,在前端加鹽 hash ,再傳到后端直接進行比對。不可行。這給黑客提供了一個方便——他不需要知道密碼就可以方便地在你這里驗證某些用戶名或郵箱是否是有效的。

策略三,既然不能從后端獲取 salt ,那簡便的方法就是使用前后端約定好的一個固定的 salt 進行 hash,比如用戶的用戶名,或者郵箱。這樣就保證了中間人監聽不到真實的密碼,同時又因為在后端又進行了一次安全的加鹽 hash ,保證了數據庫的安全性。

用什么 hash 函數?

  1. 經過安全測試的加密 hash 函數,如: SHA256, SHA512, RipeMD, WHIRLPOOL, SHA3 等等
  2. Key Stretching 算法,如: PBKDF2, bcrypt, scrypt 等
  3. 安全版本的 Unix crypt,如: $2y$, $5$, $6$

總結

  • 前端使用固定 salt 加密后送給后端
  • 后端生成強大的 salt 將前端送來的值加密儲存
  • 使用安全的 hash 函數
  • 如果可能,使用 HTTPS

參考資料


  1. Rainbow Table ?

  2. Rainbow Crack ?

  3. Salted Password Hashing - Doing it Right ?

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 現在比較流行的密碼存儲是對密碼明文做 HASH運算,把得到HASH值存儲到數據庫中。驗證過程就是再次對用戶發過來的...
    JSON_NULL閱讀 3,547評論 4 7
  • 【作者】張輝,就職于攜程技術中心信息安全部,負責安全產品的設計與研發。作為互聯網公司的信息安全從業人員經常要處理撞...
    滕的世界閱讀 2,092評論 1 2
  • 前幾天,我的一位朋友的apple賬戶被盜了。看到朋友給我發的幾張圖,頓時覺得盜取者的面目可憎。看下面的截圖,可以說...
    jaymz明閱讀 480評論 2 9
  • 1、哈希運算:即散列函數。它是一種單向密碼體制,即它是一個從明文到密文的不可逆的映射,只有加密過程,沒有解密過程。...
    YeehamChan閱讀 2,349評論 1 2
  • 伊尹是中國歷史上唯一一個由廚入相的人,是中國歷史上杰出的賢相、帝師。他輔佐商湯打敗了殘暴的夏桀,建立了商朝。 伊尹...
    deity0808閱讀 662評論 0 0