最近前天都在討論如何做身分驗證。目前討論到的三種方法有一個共同特色--依賴一開始註冊的使用者憑證(credentials)來做為身分驗證的依據,一般最常見的就是使用密碼做為使用者憑證。因此註冊會需要保存密碼,未來才有辦法判斷身分驗證是否成功,但這密碼該如何保管呢。
首先,資安所考慮的前提都是莫非定律:
「凡是可能出錯的事就一定會出錯」
因此即使資料庫放在層層防火牆之後,也設定了密碼保護,依然還是得思考資料被偷走的風險。帳號密碼外泄是非常嚴重的資安事故,這將會讓使用者無法再次信任並使用服務,服務所帶來的價值將會大打折扣。因此,必須進一步思考有沒有辦法降低帳密外洩的風險。
先前在簡介雜湊時,有提到理想的雜湊具有 preimage resistance 特性,也就是從雜湊值是無法回推當初輸入的值,這樣即使密碼外泄,攻擊者依然無法回推一開始輸入的密碼為何。
但雜湊也具備另一個特性是:相同的輸入結果會是一樣的,這代表只要預先知道 mypass
的雜湊是 a1b2c3
的話,在資料表看到 a1b2c3
的內容就可以知道該會員的密碼是 mypass
。這個預先雜湊好內容,將會是一個非常大的表,稱之為彩虹表,只要輸入雜湊值,就能回傳一個或多個原始輸入,也就是密碼。
一個簡單抵抗彩虹表的方法就是,故意把產生出來的雜湊再一次的雜湊,這能有效讓使用彩虹表要回推結果是困難的。只是這個問題也會有對應的破解法:攻擊者可以故意註冊一個帳號,並輸入 mypass
,雜湊後是 a1b2c3
,再雜湊一次是 d4e5f6
,攻擊者就會知道 mypass
這個輸入可以得到 d4e5f6
的結果。然後就能藉此重新做一張彩虹表了。
另一個比較有效抵抗的方法為灑鹽(salt)。實際的做法是,在雜湊前產生一個亂數稱之為鹽,用固定的組合方法把鹽和密碼結合在一起,最後再雜湊。因增加了密碼長度,這樣是能有效抵抗彩虹表的,因為彩虹表的資料庫也都是從較少字元的字串開始建立起的,加長之後就不容易找到了;另外因為與密碼結合的是亂數,因此攻擊者無法利用「相同的輸入結果會是一樣」的特性建立彩虹表。
但相較麻煩的是,灑鹽就成了驗證密碼的必要因素,因此也需要存到資料庫裡。另外還需要注意的是,因為鹽是用亂數產生的,因此必須使用簡介亂數裡面提到的密碼學安全的僞隨機數,來避免攻擊者預測亂數。
若我們能對密碼同時灑鹽,以及做多次雜湊運算,那就能讓攻擊者更難從雜湊結果去破解原始密碼,如 bcrypt 即是這樣的演算法。
回過頭來思考,為什麼攻擊者要偷使用者的帳密?不是都成功摸到資料庫了,直接把裡面的資料改掉就行了呀!
事實上,攻擊者是將此帳密利用來做為猜測的依據。舉例來說,使用者的 Apple 帳號與 Facebook 帳號是相同的,若攻擊者成功拿到使用者在 Facebook 的密碼之後,下一步就可以拿此帳號密碼去嘗試登入 Apple,接著就有機會為自己多買一隻 iPhone 11 了。
這才是攻擊者真正的目的,從比較不注重密碼管理的小站得到使用者的帳號密碼後,再拿去猜測大站的帳號密碼。
當了解了密碼管理的重要性後,若有發現哪個網站的忘記密碼功能可以把原始密碼寄給使用者的話,相信大家現在應該知道,這其實是件很可怕的事。