最近,國內知名安全團隊烏云漏洞平臺曝出了網易郵箱所遭到的一次大范圍攻擊,稱上億用戶的賬號密碼以及安全提示問題和答案被泄露,同時有很多用戶稱自己的Apple ID等賬號遭到竊取,密碼被改,iPhone手機被鎖定并遭到敲詐,而這些用戶都是將網易郵箱作為Apple ID來使用的。在此之前,也有一些網站出過類似的安全事故,而即便有了如此多的教訓,很多網站在保存和管理用戶密碼這件事上依然做得十分糟糕,這成了信息安全的一個嚴重的短板。
在一般的身份認證體系中,賬號(也就是ID)和密碼是配合使用的,也就是說,賬號用來識別這個用戶是誰,而密碼用來識別這個用戶是不是他本人,因為我們一般假定密碼只有用戶本人才知道。 既然如此,那么網站必須要保存用戶的賬號和密碼才能完成身份確認,而一般來說,賬號和密碼是保存在網站后臺的數據庫系統中,比如說類似這樣一張表:

這樣一來,當用戶輸入賬號user1,密碼123456時,網站就能從數據庫中找到相應的用戶,然后再檢查一下密碼對不對,密碼對了就登錄成功。不過,這種原原本本保存用戶密碼的方法其實很不安全,因為一旦攻擊者通過某種方式獲取了后臺數據庫的訪問權限,就能夠輕松地取得所有用戶的賬號和密碼。我們知道,人類的記憶力其實十分有限,大多數用戶在不同的網站上用的其實是同一套賬號和密碼,只要其中一個網站的賬號和密碼泄露出去,造成的后果是不堪設想的,因為攻擊者可以用這些信息碰運氣,說不定可以登錄其他很多網站,如果那些網站僅僅是論壇、社區、博客之類的還好,而如果是支付寶或者銀行呢?
實際上,如果時間倒回到十幾年前,恐怕大部分網站都是用這種方式來保存用戶的密碼的,想想就令人毛骨悚然呢。不過,業界對于安全的追求也并沒有懈怠,很快人們就發現這樣做實在不靠譜,于是開始想辦法進行改進。改進的思路其實也不難,既然密碼只是用來驗證對不對的,那么網站其實并不需要保存密碼本身,而是只要保存一個密碼的“指紋”就行了。這里的“指紋”用的是一種叫做“散列函數”(hash function)的算法,簡單來說,對于任意的信息,通過散列函數可以生成一段摘要,也叫散列值,散列值的長度是固定的,比如如果我們把123456輸入一個叫做MD5的散列函數,得到的散列值為:e10adc3949ba59abbe56e057f20f883e。散列函數有一個特點,那就是它是一種單向算法,也就是說,已知一段信息可以算出一個確定的散列值,但已知一個散列值卻無法算出原始信息。有了散列函數的幫助,網站可以像下面這樣保存密碼了:

當用戶登錄時,比如說輸入賬號user1,密碼123456,網站先計算出123456的散列值,然后再與數據庫中保存的散列值進行對比,如果一樣就說明登錄成功了。那么疑問來了,如果用戶輸入的密碼不是123456,就一定不能得到和數據庫中保存的一樣的散列值嗎?事實上,散列函數是一種多對一的映射,因為信息的長度可以是任意的,但散列值永遠只有固定的長度,就像把100個球放進10個袋子里,必然有至少一個袋子里面得裝多于一個的球,換句話說,必然存在兩條不同的信息,它們的散列值恰好是一樣的,這種情況叫做散列碰撞。盡管對于散列函數來說,碰撞是必然存在的,但是一個好的散列函數可以讓碰撞足夠分散,使得攻擊者無法人為地制造出碰撞,不幸的是,我們上面提到的MD5就是一個不太好的散列函數,它的抗碰撞性已經很差了,或者說已經很不安全了,然而,根據烏云平臺的描述,網易郵箱依然在使用MD5來保存用戶的密碼,也是醉了吧?在常用的散列函數中,MD5已經被淘汰,SHA-1的安全性也較弱,SHA-2是主流,而SHA-3則具備更高的安全性。
其實,這樣保存密碼還有一個問題,因為對于某個確定的散列函數,一個密碼永遠只能產生一個固定的散列值,比如說對于MD5來說,123456的散列值永遠是e10adc3949ba59abbe56e057f20f883e,那么攻擊者只要看到e10adc3949ba59abbe56e057f20f883e,就知道這個用戶的密碼是123456了。那么疑問來了,用戶的密碼千變萬化,哪有這么容易就能通過散列值一眼看出來呢?你還別說,大部分用戶的密碼都特別弱,而且,密碼與散列值的對應表是可以事先算好再從里面查的,比如說CrackStation.net這個網站上,我們就可以通過散列值來反查明文密碼, 把user2的密碼散列值c33367701511b4f6020ec61ded352059,立馬就查出來明文密碼是654321了。實際上,對于任何8位或更短的密碼,用散列值反查密碼都是一秒鐘的事情,因此對于弱密碼來說,散列實際上是形同虛設了。
怎么辦呢?既然大部分用戶的密碼都很弱,那么我們可以人為地把它們變得“強”一些,比如說在用戶的密碼前面或者后面添上一串隨機的字符,然后再計算散列值,這種方法叫做“加鹽”。加鹽之后,網站上的數據庫就變成了這個樣子:

在這里,user1的密碼123456后面被加上了一串隨機字符QxLUF1bgIAdeQX,然后將加長之后的密碼123456QxLUF1bgIAdeQX再計算散列值,得到dc8cf9d551ac50a5f167645dfee178cd。當user1登錄時,網站會先查出user1的鹽,然后將用戶輸入的密碼加上鹽算出散列值,再與數據庫中的散列值進行對比。由于每個賬號的鹽都是不同的,因此即便兩個賬號的密碼一樣,加鹽之后算出的散列值也會不一樣,而且,加鹽后算出的散列值也無法輕易地通過事先計算出的對應表來反查出明文密碼了,這大大增加了用戶密碼的安全性。應該說,加鹽并散列是現在網站保存用戶密碼的基本要求,但實際上還是有太多太多的網站沒有達到這樣的要求,這些網站也就容易成為黑客的攻擊目標。
當然,加鹽并散列也遠遠算不上完美,因為顯卡上的GPU特別擅長計算散列值,要不然為什么有一陣子大家都用顯卡來挖比特幣呢,比特幣的挖礦實際上就是計算大量的散列值。有了GPU甚至專門的計算設備,對散列值進行“暴力破解”也并非不可能,甚至加鹽的散列值也可以在有限的時間內被破解,只是一般情況下攻擊者沒有必要花那么大的代價罷了。為了進一步提高用戶密碼的安全級別,還可以采用“慢散列”、“帶密鑰散列”等技術,對于這些技術的細節我們在這里就先略過了。
說了這么多,對于普通用戶來說,在賬號密碼方面應該注意些什么呢?大體來說有下面這幾點:
1. 使用強度較高的密碼,比如8位以上,包含大小寫字母、數字和特殊字符
2. 對于不同的網站盡量使用不同的密碼,至少不能全都使用一樣的密碼
3. 使用如兩步登錄等附加認證手段
0 篇文章
如果覺得我的文章對您有用,請隨意打賞。你的支持將鼓勵我繼續創作!