昨天午休時,新浪上的一條新聞引起了我的注意。新聞中說,一家叫芝麻金融的P2P網站數據庫泄露,並且數據庫中所記錄的密碼僅經過一次哈希。雖然說我不是攻破它的白帽,更沒有仔細地研究這些泄露的數據,但如果報告所言不虛,其中所提及的各個問題實際上非常嚴重。因此我覺得有必要對如何在系統中保存密碼以及為什么要這樣做進行一個簡單的介紹。
在這篇文章中,我會簡單地介紹一下當前比較流行的對密碼進行攻擊的手段,從而讓大家了解我們平常所自認為“安全”的密碼保護手段實際上在黑客面前不堪一擊。進而我也會介紹這些攻擊的防范手段,以及業內較為認同的密碼保存方法,進而使大家做出的網站能夠更安全地保存用戶的密碼。
一場曠日持久的戰爭
記得我最早接觸網絡的時候大概是1999年的秋天,那年我初三。初三對於我來說有兩個事情要去做:准備中考,更要准備考全國理科實驗班。為了保存所有我在網上找到的競賽試卷,我申請了第一個郵箱,而郵箱的密碼只有4個字母。在那之后,各種密碼丟失的事件逐漸多了起來,例如騰迅QQ被盜,個人郵箱被盜等。因此在03年左右,那個網站就開始對密碼的長度有了強制的要求。原因很簡單:為了防止暴力破解。
而后又出現了更多更為高明的方法,例如XSS,CSRF,Session Fixation等等。而相應的安全方法也逐漸地進行着改進,如HttpOnly Cookie,HTTP請求中添加的Referer頭等。這一系列攻防所圍繞的正是用戶的身份憑證,如Session ID,用戶的密碼。因為有了它們,黑客就可以偽裝成合法用戶來悄悄地執行一系列非法行為,從而獲得利益。
因此如何安全地保護用戶的身份憑證實際上是各個網站最需要關注的問題。在使用身份憑證的任何地方,我們都需要提供對身份憑證的保護,如用戶輸入密碼時,用戶在登陸以后對網站的操作,身份憑證在網絡上的傳輸,以及密碼在數據庫中的保存等等。而在這些場景中,對身份憑證進行保護的方法也不盡相同。
在用戶輸入密碼的時候,黑客可以通過偵聽用戶輸入來完成用戶名/密碼對的竊取。在用戶登錄以后,如果Cookie中記錄了用戶名/密碼對,那么惡意人員也可以通過XSS(祥見我另一篇博客),CSRF等方式進行攻擊。而身份憑證在網絡上傳輸的時候,惡意人員可以通過中間人方式來竊取用戶的身份憑證。可以看到,為了保證用戶身份憑證的安全,一個網站需要在不同的維度對用戶的身份憑證進行保護。
而密碼在數據庫中正確地保存對於整個網站而言更為重要。一般來說,網站都會對盡一切可能保護自己的數據庫,以防止私密信息泄露或數據被惡意更改。但是一旦這些防御手段失效,那么數據庫中記錄的用戶名/密碼信息將會直接暴露在惡意用戶眼中。因此我們要對這些密碼加密,以構築保護用戶身份憑證的最后一道防線。這道防線尤為重要。因為一旦該密碼加密方法被攻破,那么黑客所得到的用戶名/密碼對都有可能被攻破。對於該網站而言,這會允許非法用戶使用這些被攻破的用戶名/密碼對執行非法操作,如虛假交易,帳戶資金轉移等。而就用戶而言,由於絕大部分用戶都習慣於在多個網站中使用同一套用戶名/密碼對,因此在一個網站中身份憑證的泄露極有可能危及到他在其它網站中的身份憑證,從而導致他在多個網站中的賬號同時被盜。因此即使是我們的網站私密性要求不高,我們也一樣需要妥當地保存用戶所設置的密碼。
不安全的密碼存儲方式
最不安全的密碼存儲方式就是使用明文在數據庫中存儲密碼。我們最可愛的CSDN就干過這事。明文密碼的意思就是並沒有經過任何處理就將用戶帳戶所對應的密碼記錄在了數據庫中。這樣做最起碼有一個不好的地方,那就是如果一個人擁有數據庫訪問權限,那么他可以很輕易地看到用戶名和其所對應的密碼。從安全的角度來說,這種密碼存儲方式是不被信任的,因為誰也無法保證擁有數據庫訪問權限的人不會為了利益主動泄露這些用戶名/密碼對。
就算是數據庫管理員可以被信任,這些密碼也可能因為數據庫管理員無意中的一個過失而泄露。例如在數據庫管理員查看數據庫的時候離開電腦去喝杯水,卻沒有在離開時鎖定電腦,那么其他人就可以查看數據庫表來得到一系列的用戶名/密碼對。
當然事情可以變得更為糟糕:如果網站上存在着SQL注入漏洞,那么黑客就可能通過SQL注入等攻擊手段來得到數據庫中用戶名和密碼列中所記錄的數據。如果這些數據並沒有經過處理,那么惡意用戶就將直接得到該用戶名所對應的密碼。
那么我們應該如何記錄這些密碼呢?相信讀者很容易就想到計算它們的哈希值。是的,這也是當前業內所最常使用的手段。那么是不是我們隨便選擇一個哈希算法就可以了呢?答案是否定的。這是因為現在已經出現了很多針對哈希計算結果的攻擊方法。
簡單地想象下面的一個情況,那就是網站有SQL注入漏洞。那么惡意攻擊者非常有可能通過該SQL注入漏洞來獲得一系列用戶名和簡單哈希算法所加密過的密碼。這些數據可能有上萬個,甚至有百萬條之多。現在他所知道的僅僅是密碼經過哈希后的結果,而哈希過程中所使用的哈希算法以及輸入他並不了解。
接下來,他要做的工作就是列出一系列可能的加密算法以及一系列最常用的密碼。這里你需要相信的是,你所能想到的哈希算法一個有經驗的黑客絕對能夠想到。而常用的密碼,上網上搜索“最常用的密碼”,或者找一本密碼字典就足夠了。在一個包含了上萬用戶的網站中,有接近百分之百的幾率出現形如“12345qwer”這樣的密碼。
OK,現在他的工作開始了:從密碼字典中選擇一個最常用的密碼,並通過他所搜集到的哈希算法分別進行加密。接下來,將這些加密結果與他剛剛所得到的那些哈希過的密碼進行比較。該過程中,一次哈希計算所得到的結果可以與其所得到的多個密碼的哈希值同時進行比較。在樣本數目非常大的情況下,該結果的命中概率將變得非常大。這就是攻擊者在執行攻擊時的一個非常大的優勢:基於極多的樣本並行地執行攻擊嘗試。
一旦一個哈希算法所得到的結果和任意一個密碼的哈希值匹配成功,那么當前所使用的加密算法就非常有可能是系統所使用的加密算法。在使用不同的常用密碼多次嘗試以后,你所使用的加密算法在黑客眼中就已經非常明顯了。如果這個加密算法是雙向的,那么可以說,這個被攻擊的系統已經完全淪陷了。
而這種攻擊甚至可以被加速:在嘗試猜測加密算法之前,該惡意用戶會首先對這些常用的算法以及常用密碼計算對應的哈希值,並直接使用這些哈希值與所得到的哈希過的密碼進行比較。這甚至省略了計算哈希的時間,並使得這些哈希計算結果可以被重用。這種攻擊有一個特殊的名字:Rainbow Table Attack。
展開防御
是不是覺得有點恐怖?不用擔心,有攻擊就有防御。攻防之間的斗智斗勇才是安全領域最有意思的事情。
現在我們來想想這種攻擊成功的必要條件:一個黑客能夠猜中的加密算法,和一個黑客同樣能夠猜中的密碼。這兩個必要條件都是基於概率的:黑客猜中加密算法的概率較高,而且在用戶數目較大的情況下,系統中存在形如“12345qwer”的密碼的概率非常大。而要想阻止黑客攻擊,我們就需要讓我們的網站不再具有這種必要條件。
對於第一個必要條件,我們的解決方案並不是要自創一個新的加密算法。這從安全的角度來說是完全不安全的。在后面的章節中我們會對這種做法為什么不安全進行講解。而我們所需要的解決方案則是讓黑客猜不中我們所使用的加密算法,也就是使用多種加密算法進行加密。這樣即使是同樣的密碼也會產生不同的結果,減小黑客猜中哈希算法的概率。而對於第二個必要條件,我們則可以對密碼本身進行一次增強。該增強算法需要盡量增加密碼本身的復雜度卻基本不產生密碼碰撞(即增加了復雜度的兩個密碼最終變成了一樣的密碼)。這樣即使黑客猜對了增強后的密碼以及對其加密所使用的算法,那么他也無法知道原始密碼到底是什么樣子的。而這一步,業內的建議也是要由標准類庫來完成。這其中的顧慮實際上與不要自創加密算法一樣。
那么黑客就剩下一個攻擊方法了,那就是硬猜,也就是常說的暴力破解(Brute Force Attack)。一個一個地猜雖然是一個笨方法,但是隨着猜測次數的增加,猜中密碼的概率也會逐漸增大。為了避免這種攻擊成功,我們需要防止黑客快速地計算密碼所對應的哈希值。一個簡單的哈希函數,如MD5,在一個現代的設備上可以每秒運行上百萬次,甚至上億次。也就是說,如果我們使用一個簡單的哈希函數,那么在一秒鍾內可以有成千上萬個哈希值參與比較。結果就是幾十秒鍾之內用戶所使用的密碼就可能被猜測出來了。因此對密碼進行加密的方法需要較為緩慢,以增加這種攻擊的難度。
但是呢,黑客手中還有一個利器,那就是並行計算。現在,構建一個可以進行並行計算的系統已經不再那么昂貴,甚至只需要一個支持並行計算的普通的GPU。因此就算是計算一次密碼的哈希值較為緩慢,黑客可以通過同時計算多個密碼所對應的哈希值進行加速,使得暴力破解的速度幾十倍地增長。解決方法很簡單:選擇一個不支持並行計算的哈希算法。
OK,現在看來,我們已經對黑客所常用的攻擊方法進行了防御。那么就讓我們來總結一下進行加密的哈希算法所需要擁有的特征:
- 哈希算法需要是單向的。因為一旦使用了雙向哈希算法,那么通過反向計算得到的字符串常常只包含數字,字母以及常用的符號。這在黑客眼中是一個非常明顯的哈希算法猜測(接近)成功的信號。接下來,他只需要對各個哈希值反向計算即可得到相應的密碼。
- 哈希算法的碰撞需要盡量少。因為如果N個不同的密碼能夠產生同一個哈希值,那么它被攻破的概率就大了N - 1倍。
- 減慢哈希的計算速度。這不僅僅需要減慢哈希的計算速度,還需要令哈希不支持並行計算。
- Salt。Salt就是我們剛剛提到的用來在加密系統中用於選擇哈希函數並增強密碼的組成。
Salt簡介
相信讀者對上面所提到的Salt不是很理解。例如:Salt中包含的值是什么?如何使用?存儲在哪里?
一個比較受歡迎的生成Salt的方法就是得到一個128位或更長的隨機整形數據並將其轉化為字符串,或者是使用隨機生成的UUID。在用戶第一次設置其所使用的用戶名和密碼的時候,系統將為其生成一個Salt,並使用該Salt以及系統的加密方法計算用戶密碼的哈希值,並將該哈希值及Salt存在數據庫中。而在用戶再次登陸的時候,系統將根據用戶所輸入的密碼以及之前為用戶所生成的Salt再次使用系統的加密方法計算哈希值,並將計算結果與數據庫內所保存的哈希值比較。如果兩個哈希值相等,那么就表示用戶所輸入的密碼是正確的,並登陸成功。
因為每次對密碼的操作都會用到這個Salt,因此我們常常將它與用戶名/密碼對同時存儲在數據庫中。在加密過程中,Salt也將會作為我們所使用的加密方法的一個輸入,以用來選擇加密方法中所使用的哈希函數,並增強用戶所使用的密碼。從而使得對一系列密碼的字典攻擊以及Rainbow Table攻擊失效。
或許你還是不是非常理解它是如何使字典攻擊及Rainbow Table攻擊失效的。那么我們假設現在一個黑客已經拿到了一系列的用戶名/密碼哈希值組合。在攻擊時,他首先選取一個可能的密碼password,並使用選定的哈希函數hash()進行加密操作hash(password),並與所有的密碼哈希值進行比較。一旦成功,那么就基本上能確定選對了哈希算法。而如果哈希過程中使用了Salt,那么他所得到的信息就是用戶名/密碼哈希值/Salt。由於每個用戶的Salt並不相同,因此他需要根據各個用戶的Salt值來計算哈希值,即hash(salt[0], password),hash(salt[1], password)等等。這使得通過一次計算就可以和多個哈希值進行比較變得不再可能。又由於哈希算法較為緩慢,因此黑客成功攻擊所需要的時間便大大增加。
前面已經說過,Rainbow Table攻擊是通過提前計算各個可能密碼的哈希值來縮短時間的。而現在參與加密的Salt則是一個隨機字符串,如“js98LP6h”,顯然Rainbow Table中所列出的可能的密碼將不會包含這種形式的密碼,從而使得Rainbow Table攻擊失效。
對Salt的一個常見誤區就是對Salt的使用可以增加破解單個密碼的難度。其實並不然。一般情況下,Salt都和哈希過的密碼一樣存在於數據庫中。因此惡意人員在訪問到數據庫中所記錄的用戶名/密碼哈希值對的同時也能訪問到哈希所需要使用的Salt。因此在嘗試攻擊時,其可以直接使用他所得到的Salt和密碼字典中列出的各可能密碼結合在一起計算哈希值。對這種攻擊的防御是通過減慢哈希計算速度來完成的,而Salt則是用來防御並行攻擊,即一次計算哈希就可以和多個哈希進行比較。
選擇合適的加密方法
實際上,業內已經有很多用來對密碼進行加密的方法了,如PBKDF2,bcrypt,scrypt等。這些加密方法各有優劣。因此在需要保護用戶的密碼時,我們需要盡量從這些標准加密方法中選擇。在使用這些加密方法時,您還需要指定迭代次數等眾多參數。這些參數對於網站本身來說都是機密,因此不要將它們存在數據庫中,以免在數據庫泄露的時候同時丟失這些設置,進而導致這些加密算法的使用細節泄露,減弱加密方法的安全性。
反過來,自行定義一個加密方法實際上是並不被建議的。實際上,設計一個加密算法是一個非常嚴肅的事情。我們所熟知的各種加密算法,如SHA算法集,實際上都經歷了很嚴謹地論證才被宣布是安全的。首先密碼學專家要經過非常細致地研究才提出加密方案,然后在業內的各種討論中,這些加密方案將被彼此進行比較,競爭,並最終經過數年時間驗證才宣布是安全的。
也正是因為我們自己並不是密碼學專家,因此我們所創建出來的加密算法相較於這種經過嚴謹考驗的各個加密算法而言將是非常不安全的。
一個比較容易讓人產生疑惑的就是加密算法中的碰撞。我們常常說MD5已經不再認為是安全的加密算法了。這是因為惡意人員可以很容易地找到一系列輸入,使它們所產生的MD5是相同的。這在一系列驗證領域中是不安全的,如文件的校驗。因為在進行MD5校驗的時候,惡意軟件可以通過使它的MD5與目標文件相等而繞過MD5校驗。但是密碼的加密算法所要求的則是在經過加密后不能逆向解析出被加密的密碼,因此它仍然是一種安全的加密算法。只是由於其計算速度過快,因此不建議被單獨使用。
本文章由專業律師事務所執行版權保護。授權范圍僅限於個人轉載,並需要在題目中標明轉載
轉載請注明原文地址:http://www.cnblogs.com/loveis715/p/4417526.html
商業轉載請事先與我聯系:silverfox715@sina.com