用什麼樣的密碼比較安全呢?從基礎談起!

今天來講一下各大網站保存密碼的方式。如果非電腦科學領域的人,只是來湊湊熱鬧,那可以直接往下拉到結論,我直接告訴你,你的密碼安不安全?
評論
Antoine Vincent Jebara, of Myki, demonstrates his password manager app during the Startup Battlefield Competition of the 2016 TechCrunch Disrupt in San Francisco, California, U.S. September 13, 2016.  REUTERS/Beck Diefenbach - RTSNMHI
Antoine Vincent Jebara, of Myki, demonstrates his password manager app during the Startup Battlefield Competition of the 2016 TechCrunch Disrupt in San Francisco, California, U.S. September 13, 2016. REUTERS/Beck Diefenbach - RTSNMHI
評論

本文作者 姜柏宇 ,本職看 NBA 、買彩卷,兼職軟體工程師,喜歡挑戰新事物,偶爾做做 side project,樂於分享成長歷程,目標是能成為一個獨當一面的系統架構師,並朝著這遠大目標緩慢前進。INSIDE 獲作者授權轉載,原文刊登於 作者 Blog

今天來講一下各大網站保存密碼的方式。如果你是工程師,而且知道 hash + salt 是什麼意思,可以直接跳過這篇文章。如果非電腦科學領域的人,只是來湊湊熱鬧,那可以直接往下拉到結論,我直接告訴你,你的密碼安不安全?

Hash your password

如果你在做一個網站後端開發,一位使用者創帳號的時候,你要用一個固定的 hash function 在資料庫裡存 hash 過後的密碼,每次使用者登入的時候,把使用者給你的密碼 hash 之後,看看跟你資料庫的那個一不一樣,因為 hash 是不能 reverse 的,所以即使哪天資料外洩,駭客也無法知道你的明碼。

HASH(plain_text) == saved_string

聽起來挺 OK 的啊?

一開始的確是沒什麼問題,所以一開始網站沒有給太多密碼複雜度的限制。但隨著記憶體空間越來越便宜,駭客可以先把所有可能的 hash 結果存起來,再一一比對他從資料庫拿到的 hashed_value 跟他先算好的表有沒有吻合,如果能成功對應到你的密碼,就等於被破解了。

光說不練假把戲,來點數學吧!十年前安全性沒人要求的時候,一般人的密碼都挺短的,而且都是英文 + 數字。假設長度是 7 的話,那所有的可能明碼也才 (26+10)^7 種,假設現在是用 md5 hash(hash 完的長度是 128bits):

(1*7 + 128 / 8) * (26+10) ^7 bytes = 1.8 TB

意思是我要存下所有的 mapping,其中 1*7 是明碼,128/8 是 hash 過的密碼 byte 大小。在 2017 年,這個容量簡直輕鬆愉快啊!那麼該怎麼辦呢?

Salt(加鹽)

這個時候就需要加點隨機的東西,增加亂度了。解法就是在每個人創帳號密碼的時候,隨機生成一個字串,加在明碼後面或是穿插明碼其中,Verify 的時候也一樣,只要伺服器記得每個人的隨機字串就可以:

HASH(plain_text + salt) == saved_string

最好連 salt 的長度也是 random 的(當然要越長越好),這樣的話駭客就必須所有的長度都有個表,那其實更快的方式是直接把 username 或是 email 加進來 hash。基本上就是增加原本字串的難度,畢竟所有 md5 的 output 是 2^128,沒有一個 machine 可以記得了所有的 input,而增加難度最簡單的方 就是增加長度,長度每加一,難度變 36 倍。

另一種就是增加可能的字母,加上大寫(26)、加上特殊符號(32)(Password special character),底數直接從 36 -> 94,那自然就更難了。

所以為什麼大網站都會要求你用長度最少 12 的密碼,最少要有一個大寫、一個小寫、一個特殊字母、一個數字,這樣如果 hacker 萬一哪天破解了這個網站的資料庫,拿到了 hash 過的密碼,大概未來一、兩年內,都還不會有事。如果再加上 random(隨機)長度的 salt,基本上就不太可能用這種暴力法破解。

問題

Q:既然 salt 是存在資料庫的明碼,那遲早有一天記憶體大到可以 cover 長度 10 的密碼,那再把它減去 salt,不就得到 user 的密碼了嗎?

A:所以 salt 有兩個目的:其中第一是讓 hash 前的字串總長度拉長,這樣即使使用者給的長度不夠,還是可以增加暴力破解的難度。

舉個例子,如果 user 密碼長度是 10,salt 長度是 5,那總長度是 15 的密碼,就不太可能用建表暴力解再減 salt 的方式。

Q:總長度 15 的不好建,10 的總可以了吧?他不就可以把所有排列組合 + salt 建表嗎?

A:答對了!這就是為什麼 salt 要 random!

salt 的第二個目的,是可以把損失降到最低。那個駭客一次還是只能破解一個人的密碼,因為他必須對每個新的 salt 重新建表,這樣可以幫資安部門爭取時間,叫使用者趕快改密碼。

如果我是駭客

當然不是說什麼順便 hack 生日這種資訊,排列組合猜一猜,這是要猜到什麼時候?這個部落格怎麼可能講出這麼沒格調的話?

基本概念很簡單,因為大家懶。十年前大家需要辦帳號時都有過密碼,而當時對於密碼的要求不高,所以很有可能大多數的密碼都只有小寫英文(因為大腦不喜歡英文單字中參雜數字這種不協調感)。然後呢?之後安全意識抬頭,大家要求要有大寫字母和特殊字元,可是大家又不想改原本的密碼太多,所以就很簡單的加在原本的密碼後面。考慮這些歷史因素的話,我就會先創建所有的可能密碼的 hash,大概長這樣:

abcdefgH0!

(前七~九個是小寫英文,後面一個大寫英文、一個數字,最後面一個特殊符號!)

我相信這樣應該會中一些(XD),但如果網站有加 salt,那就什麼都不管用了,這就是為什麼你身為開發者要加 salt 的原因,就像短版效應一樣:

你密碼的安全度,取決於所有你用同一個密碼的網站裡,安全做最差的網站的安全度。

結論:講這麼多,所以我的密碼到底夠不夠強呢?

大概念就是:越長越好。這是最重要的因素,其中穿插大小寫互換跟數字,然後特殊符號穿插其中(不要擺最前也不要擺最後),雖然很麻煩,但打一下就習慣了。

所以你的密碼到底有多安全呢?長度 10 都是英文小寫,跟長度 8 但可以有特殊符號,哪個比較安全呢?

簡單的計算方式就是:

(1*n + 128 / 8)*(26 or 36 or 62 or 94)^n

n 是密碼長度

26 就是只有小寫英文

36 就是只有小寫英文 + 數字

62 就是只有大小寫英文 + 數字

94 就是只有大小寫英文 + 數字 + 特殊符號

我的經驗法則是:如果你的數字小於 10^24,那大概就跟 Jon Snow 一樣:

那強烈建議你,換個密碼囉!