今天在weibo.com上看到一則消息說, 用md5/sha1+salt的方法保存密碼是不安全的. 文章中用連續9個Bcrypt加感嘆號來強調, 保存密碼一定要用Bcrypt.
這個消息非常應景, 因為csdn.net兩天前把數據庫弄丟了, 里面的密碼是明文保存的. 我對密碼和安全沒有研究, 但是在我粗淺的記憶中, hash+salt是非常主流的方法. 如果說這樣也不靠譜, 那我正好借這個機會復習一下.
我把密碼安全分為兩個等級, 黃色和紅色. 當數據庫丟失后, 能算出一個可以用來登陸的密碼, 這個密碼不一定是原文, 但是保證可以通過驗證, 這個算達到黃色. 如果還能算出原文, 這個就達到了紅色.
紅色比黃色嚴重得多. 因為用戶往往在多個網站上都使用了同樣的密碼. 如果攻擊者拿到原文, 這等於說多個網站都受到影響了. 如果拿不到原文, 那攻擊者最多也就破壞當前這個泄露數據庫的網站, 不會影響到其它網站.
在我看來, 很多用戶對安全的要求是, 如果我使用的網站A不幸陣亡了, 只要密碼原文不泄漏, 就不會傷及我使用同樣密碼注冊的其它網站. 至於在網站A上面的任何損失, 我都不在乎了.
在這個背景下, 我的本能看法是, 用hash+salt難道不能實現上述的要求, 一定要使用Bcrypt嗎?
如果仔細分析一下黃色和紅色的定義, 會發現一個問題. 對於一個給定的可以用來登陸的密碼, 如何判定這個密碼就是原文呢? 這里的解決方法通常就是對比字典, 或者觀察這個密碼的字面特性.
既然牽涉到字典, 情況就更加有趣了. 破解密碼的兩種方法分別是窮舉和字典. 我從wikipedia以及介紹上Bcrypt的資料上了解到, 如果只是窮舉, hash+48位的salt算起來已經比較困難了. 文章中說,半分鍾可以窮舉完所有的6位密碼。可以想象,如果加上一個48位的salt, 按照復雜度的增加比例,要窮舉完所有可能的密碼,至少還是要按月算計算的吧。
在這樣的情況下,如果要想得到密碼原文,首先需要花大量時間把很多可能的密碼都算出來,然后才能挑選原文。就算用集群計算機,這個也需要幾天時間吧。我想也不是所有用戶都能享受到這個待遇的吧。如果這條路走不成,那就只有反過來,根據現成的字典來算。如果不幸使用了字典中能查到的就活該倒霉了。
當然,上面的假設是竊密者不知道salt的情況. 如果salt一起丟了的話, salt對單個用戶的保護就完全丟失了。這個情況下salt的作用是讓竊密者必須對每個用戶都單獨計算。但是即便是這樣,如果密碼稍微復雜一點,用8位數字字母組合,暴力走一遍也需要60的8次方除以700M,結果是66小時。實際時間應該比這個更長一點,因為這個時間沒有考慮到長度小於8位的。
為了證明我的想法,我特意花了20大洋,到http://cmd5.com/ 這個網站上買了4次破解機會。我提交了一個123456作為原文,12位salt計算出來的密文。另外再提交了一個8位包含大小寫數字和一個@字符的原文,12位同樣salt計算出來的密文。提交的時候把salt一起提交的。明天我再看看他能計算出怎樣的結果。
根據上面的分析,使用hash+salt的話,在不同情況下有不同的結果:
如果salt沒有丟,除非你的確是機要人員,基本上安全。普通cracker不會花這么多精力來搞你。
如果salt丟了,情況根據密碼復雜度來看。簡單密碼就秒破到原文了。對於8位普通程度的密碼,比如315@hkBJ這樣的密碼,對方可以在幾個小時內走到黃色等級,但需要2天時間才能開始分析原文。
再來看看使用Bcrypt的話,情況會有如何變化。Bcrypt其實是通過增加計算密文的成本來換取安全。使用Bcrypt,默認設置下登陸時間會增加5個數量級,破解成本也會增加5個數量級。所以使用Bcrypt的話,根據我的理解,情況分為兩種:
如果Bcrypt的salt丟了,使用簡單密碼或者字典中存在的密碼,秒破的時間從小於一分鍾變到幾天時間。所以使用簡單密碼還是不安全的。但是如果salt沒有丟,或者即便丟了但是使用了稍微復雜的密碼,要達到黃色平均時間都上年了,要到紅色就不可能了。
所以,總的結論是:
如果使用不安全的密碼,什么都幫不上忙。
如果使用hash+salt, 事故發生的時候,如果salt丟了,算法也沒有特殊的地方,只要遇上專業人員,如果密碼在8位以內,要拿到可能的密碼原文,基本上是一個星期的工作。我估計花1W RMB應該能找到人干。但是如果密碼里面的特殊字符多一點,或者長度再長一點,基本就安全了,除非你是重要人物。
如果使用Bcrypt,那當然最好了。丟了什么都不怕。但是,使用Bcrypt的成本是,負責做認證的服務器,可能要擴容幾十倍或者幾百倍哦,它是靠把計算成本提高5個數量級來換取安全的。
作為設計人員,使用Bcrypt肯定安全很多。但是安全的代價是性能。小型的應用沒有問題,但如果每秒峰值要做到上萬次登陸呢?畢竟每次登錄默認0.3秒的開銷擺在那里,一萬次是3000秒,如果要保證登陸在1秒內完成,須要3000個服務器啊。所以在大一點的應用上使用Bcrypt,登陸服務器的設計和Bcrypt參數的配置都要仔細考量。在這樣的權衡下,我覺得使用hash+salt仍舊是不錯的選擇. 前提是要做到如下幾點: 把salt單獨存放,使用SHA512, 獨立設計hash方法比如把salt插到password中間,多做幾層hash,另外把代碼和數據庫分開存放。
下面這篇文章詳細分析了Bcrypt的性能。得到的結論是除非你給五角大樓寫程序,通常的SHA512足夠好了。
“The level of security your app provides is really up to you. Unless you are writing apps for the pentagon, the default Sha512 is fine. Regardless, Authlogic shouldn’t get in your way or make decisions for you, so if you feel BCrypt is necessary then go for it.”
作為用戶來講,主要是做到兩點。首先要使用比較復雜的密碼,其次是不同安全級別的網站要使用不同的密碼。比如國內網站和國外網站堅決不用一樣的。銀行密碼不用一樣的。如果注冊使用郵箱作為源,那這個源的密碼要特別設計。
以上就是今天晚上研究了4個小時的收獲。看來我7年前了解到的hash+salt,在新時代的計算能力面前的確比較脆弱了。我算重新入門了一次。多謝 @左耳朵耗子.
相關資料:
http://codahale.com/how-to-safely-store-a-password/
http://en.wikipedia.org/wiki/Bcrypt
http://en.wikipedia.org/wiki/Rainbow_table
http://en.wikipedia.org/wiki/Salt_(cryptography)