網站資料庫用什麼樣的密碼加密的演算法才是最安全的?


如果你沒有耐心看完後面的回答,那麼記住一個詞:bcrypt,然後馬上回去開始著手升級你的密碼系統。具體如何實施請自行搜索,或者找靠譜的安全專家幫忙。

-------- 啰嗦版分割線 --------

沒有『最安全』的加密演算法。再安全的演算法也敵不過糟糕的管理。所以在琢磨怎麼加強你的密碼系統前,先確認你的伺服器是否安全、是否已經被掛馬。什麼?你跑的現成的 PHP 應用?那十有八九你已經被入侵了很多次了……怎麼確保伺服器安全?呵呵,那答案就沒那麼簡單了,多少人靠著這個吃飯呢。如果你不知道怎麼確保伺服器安全並且願意花錢解決這個問題,這裡插個小廣告:http://www.anquanbao.com/

好了,現在假設你已經確保你的伺服器和應用都是安全的了。怎麼加強密碼系統?先預習我在這裡的另外兩個回答:

  1. http://www.zhihu.com/question/19983237?nr=1thread_id=19983237

  2. http://www.zhihu.com/question/19790488

別等,去看完再回來接著往下讀。

看完上面兩個回答,你應該已經知道了為什麼不能存明文密碼、為什麼簡單的快速哈希演算法(MD5 之流)加密都不靠譜了。那解決方案也很簡單,用複雜的哈希演算法(如果你不知道哪個更好,記住用 bcrypt 就對了),然後強制用戶不能輸入那些常見密碼(123456、111111 什麼的,隨便找個密碼庫做下統計去除排名前20位的就行)、規定至少使用12位的數字字母符號組合密碼可以有效避免你的資料庫泄露後帶來的麻煩。

但是為什麼你的資料庫會泄露呢!!??是不是伺服器已經被人掛馬了啊親?所以我們又回到了第一步……


其實把MD5或者SHA1這類的摘要演算法稱作加密演算法是不太妥當的,因為這樣子「加密」過後的數據根本不能解密。

摘要演算法是一個帶向不可逆的演算法,用來保證用戶的驗證信息不會被管理人員或者其他獲取,即管理員或者黑客即使得到了資料庫的內容,也無法獲得口令等認證信息然後登錄。

不管是使用MD5還是SHA1,即使我們假設這種摘要演算法足夠安全,也不能保證口令不會被猜出來,通過彩虹表可以很快的計算出來一部分的口令。

有兩種方法比較好,一種是多次迭代,將口令hash的結果再hash若干次,對於口令這種很短的消息,多hash個七八次也是不要緊的,這樣子攻擊者就很難通過彩虹表猜測口令了。

還有一種做法是,在口令後面附帶一段消息,比如說帶上網站的網址,或者某一句話。這樣子,黑客手上的彩虹表就不能重新利用,必須重新計算一次。這樣子也可以對抗利用彩虹表的攻擊。

另外,這兩種方法是可以加在一起使用的。


argon2 , scrypt, bcrypt, pbkdf2 都可以。
首推 argon2, scrypt。
使用 libsodium 最方便,文檔見:https://download.libsodium.org/doc/password_hashing/index.html

就調2個函數即可:

crypto_pwhash_str()
crypto_pwhash_str_verify()

https://gist.github.com/byronhe/429264723b1dc1b6ab64

用戶密碼哈希 · libsodium 中文文檔

Argon2 函數 · libsodium 中文文檔

其它語言也有 libsodium 的綁定。


偷懶的人其實更喜歡下面的方式:

$salt = substr(uniqid(rand()), -6);

$password=md5(md5($password).$salt);


以下是在公司內部技術分享時總結的,希望對你有用:

我們資料庫的許可權管理十分嚴格,敏感信息開發工程師都看不到,密碼明文存儲不行嗎?

不行。存儲在資料庫的數據面臨很多威脅,有應用程序層面、資料庫層面的、操作系統層面的、機房層面的、員工層面的,想做到百分百不被黑客竊取,非常困難。

如果密碼是加密之後再存儲,那麼即便被拖庫,黑客也難以獲取用戶的明文密碼。可以說,密碼加密存儲是用戶賬戶系統的底褲,它的重要性,相當於你獨自出遠門時縫在內衣里錢,雖然你用到他們的概率不大,但關鍵時刻他們能救命。

那用加密演算法比如AES,把密碼加密下再存,需要明文的時候我再解密。

不行。這涉及到怎麼保存用來加密解密的密鑰,雖然密鑰一般跟用戶信息分開存儲,且業界也有一些成熟的、基於軟體或硬體的密鑰存儲方案。但跟用戶信息的保存一樣,想要密鑰百分百不泄露,不可能做到。用這種方式加密密碼,能夠降低黑客獲取明文密碼的概率。但密鑰一旦泄露,用戶的明文密碼也就泄露了,不是一個好方法。

另外,用戶賬戶系統不應該保存用戶的明文密碼,在用戶忘記密碼的時候,提供重置密碼的功能而不是找回密碼。

保存所有密碼的HASH值,比如MD5。是不是就可以了?

不是所有的HASH演算法都可以,準確講應該是Cryptographic Hash。Cryptographic Hash具有如下幾個特點:

  1. 給定任意大小任意類型的輸入,計算hash非常快;
  2. 給定一個hash,沒有辦法計算得出該hash所對應的輸入;
  3. 對輸入做很小改動,hash就會發生很大變化;
  4. 沒有辦法計算得到兩個hash相同的輸入;

雖然不是為加密密碼而設計,但其第2、3、4三個特性使得Cryptographic Hash非常適合用來加密用戶密碼。常見的Cryptographic Hash有MD5、SHA-1、SHA-2、SHA-3/Keccak、BLAKE2。

從1976年開始,業界開始使用Cryptographic Hash加密用戶密碼,最早見於Unix Crypt。但MD5、SHA-1已被破解,不適合再用來保存密碼。

那我保存用戶密碼的SHA256值。

不行。黑客可以用查詢表或彩虹表來破解用戶密碼。注意是破解密碼不是破解sha256,能根據sha256破解密碼的原因是,用戶密碼往往需要大腦記憶、手工輸入,所以不會太複雜,往往具有有限的長度、確定的取值空間。

  • 短的取值簡單的密碼可以用查詢表破解

比如8位數字密碼,一共只有10^8=100000000種可能。一億條數據並不算多,黑客可以提前吧0-99999999的sha256都計算好,並以sha256做key密碼為value存儲為一個查詢表,當給定sha256需要破解時,從表中查詢即可。

  • 取值相對複雜,且長度較長的密碼,可以用彩虹表破解

比如10位,允許數字、字母大小寫的密碼,一共有(10+26+26)^10~=84億億種可能,記錄非常之多難以用查詢表全部保存起來。這時候黑客會用一種叫做彩虹表的技術來破解,彩虹表用了典型的計算機世界裡解決問題的思路,時間空間妥協。在這個例子裡面,空間不夠,那就多花一些時間。在彩虹表中,可以將全部的sha256值轉化為長度相同的若干條hash鏈,只保存hash鏈的頭和尾,在破解的時候先查詢得到sha256存在於哪條hash鏈中,然後計算這一條hash鏈上的所有sha256,通過實時比對來破解用戶密碼。

上圖圖展示了一個hash鏈長度為3的彩虹表,因為在hash鏈中需要將hash值使用R函數映射回密碼取值空間,為了降低R函數的衝突概率,長度為K的hash鏈中,彩虹表會使用k個R函數,因為每次迭代映射回密碼空間使用的R函數不一樣,這種破解方法被稱作彩虹表攻擊。

實際的情況Hash鏈要比遠比上例更長,比如我們的例子中全部的84億億個sha256存不下,可以轉化為840億條長度為1千萬的sha鏈。對彩虹表原理感興趣的話,可以閱讀它的維基百科。

網路上甚至有一些已經計算好的彩虹表可以直接使用,所以直接保存用戶密碼的sha256是非常不安全的。

怎樣避免彩虹表攻擊?

簡單講,就是加鹽。一般來講用戶密碼是個字元串key、鹽是我們生成的字元串salt。原來我們保存的是key的hash值HASH(key),現在我們保存key和salt拼接在一起的hash值HASH(key+salt)。

這樣黑客提前計算生成的彩虹表,就全都失效了。

鹽應該怎麼生成,隨機生成一個字元串?

這是個好問題,並不是加個鹽就安全了,鹽的生成有很多講究。

  • 使用CSPRNG(Cryptographically Secure Pseudo-Random Number Generator)生成鹽,而不是普通的隨機數演算法;

CSPRNG跟普通的隨機數生成演算法,比如C語言標準庫裡面的rand()方法,有很大不同。正如它的名字所揭示,CSPRNG是加密安全的,這意味著用它產生的隨機數更加隨機,且不可預測。常見編程語言都提供了CSPRNG,如下表:

編程語言CSPRNGC/C++CryptGenRandomJavaJava.security.SecureRandomPHPmcrypt_create_ivErlangCrypt:strong_rand_bytesLinux/Unix上的任何編程語言讀取/dev/random

  • 鹽不能太短

想想查詢表和彩虹表的原理,如果鹽很短,那意味著密碼+鹽組成的字元串的長度和取值空間都有限。黑客完全可以為密碼+鹽的所有組合建立彩虹表。

  • 鹽不能重複使用

如果所有用戶的密碼都使用同一個鹽進行加密。那麼不管鹽有多複雜、多大的長度,黑客都可以很容易的使用這個固定鹽重新建立彩虹表,破解你的所有用戶的密碼。如果你說,我可以把固定鹽存起來,不讓別人知道啊,那麼你應該重新讀一下我關於為什麼使用AES加密不夠安全的回答。

即便你為每一個用戶生成一個隨機鹽,安全性仍然不夠,因為這個鹽在用戶修改密碼時重複使用了。應當在每一次需要保存新的密碼時,都生成一個新的鹽,並跟加密後的hash值保存在一起。

注意:有些系統用一個每個用戶都不同的欄位,uid、手機號、或者別的什麼,來作為鹽加密密碼。這不是一個好主意,這幾乎違背了上面全部三條鹽的生成規則。

那我自己設計一個黑客不知道的HASH演算法,這樣你的那些破解方法就都失效了。

不可以。

首先如果你不是一個密碼學專家,你很難設計出一個安全的hash演算法。不服氣的話,你可以再看一遍上面我關於Cryptographic Hash的描述,然後想一想自己怎麼設計一個演算法可以滿足它的全部四種特性。就算你是基於已有的Cryptographic Hash的基礎上去設計,設計完之後,也難以保證新演算法仍然滿足Cryptographic Hash的要求。而一旦你的演算法不滿足安全要求,那麼你給了黑客更多更容易破解用戶密碼的方法。

即便你能設計出一個別人不知道的Cryptographic Hash演算法,你也不能保證黑客永遠都不知道你的演算法。黑客往往都有能力訪問你的代碼,想想柯克霍夫原則或者香農公里:

密碼系統應該就算被所有人知道系統的運作步驟,仍然是安全的。

為每一個密碼都加上不同的高質量的鹽,做HASH,然後保存。這樣可以了吧?

以前是可以的,現在不行了。 計算機硬體飛速發展,一個現代通用CPU能以每月數百萬次的速度計算sha256,而GPU集群計算sha256,更是可以達到每秒10億次以上。這使得暴力破解密碼成為可能,黑客不再依賴查詢表或彩虹表,而是使用定製過的硬體和專用演算法,直接計算每一種可能,實時破解用戶密碼。

那怎麼辦呢?回想上面關於Cryptographic Hash特性的描述,其中第一條:

給定任意大小任意類型的輸入,計算hash非常快

Cryptographic Hash並不是為了加密密碼而設計的,它計算非常快的這個特性,在其他應用場景中非常有用,而在現在的計算機硬體條件下,用來加密密碼就顯得不合適了。針對這一點,密碼學家們設計了PBKDF2、BCRYPT、SCRYPT等用來加密密碼的Hash演算法,稱作Password Hash。在他們的演算法內部,通常都需要計算Cryptographic Hash很多次,從而減慢Hash的計算速度,增大黑客暴力破解的成本。可以說Password Hash有一條設計原則,就是計算過程能夠按要求變慢,並且不容易被硬體加速。

應該使用哪一種Password Hash?

PBKDF2、BCRYPT、SCRYPT曾經是最常用的三種密碼Hash演算法,至於哪種演算法最好,多年以來密碼學家們並無定論。但可以確定的是,這三種演算法都不完美,各有缺點。其中PBKDF2因為計算過程需要內存少所以可被GPU/ASIC加速,BCRYPT不支持內存佔用調整且容易被FPGA加速,而SCRYPT不支持單獨調整內存或計算時間佔用且可能被ASIC加速並有被旁路攻擊的可能。

2013年NIST(美國國家標準與技術研究院)邀請了一些密碼學家一起,舉辦了密碼hash演算法大賽(Password Hashing Competition),意在尋找一種標準的用來加密密碼的hash演算法,並藉此在業界宣傳加密存儲用戶密碼的重要性。大賽列出了參賽演算法可能面臨的攻擊手段:

  • 加密演算法破解(原值還原、哈希碰撞等,即應滿足Cryptographic Hash的第2、3、4條特性);
  • 查詢表/彩虹表攻擊;
  • CPU優化攻擊;
  • GPU、FPGA、ASIC等專用硬體攻擊;
  • 旁路攻擊;

最終在2015年7月,Argon2演算法贏得了這項競賽,被NIST認定為最好的密碼hash演算法。不過因為演算法過新,目前還沒聽說哪家大公司在用Argon2做密碼加密。

一路問過來好累,能不能給我舉個例子,大公司是怎麼加密用戶密碼的?

今年(2016)Dropbox曾發生部分用戶密碼數據泄露事件,當時其CTO表示他們對自己加密密碼的方式很有信心,請用戶放心。隨後,Dropbox在其官方技術博客發表名為《How Dropbox securely stores your passwords》的文章,講述了他們的用戶密碼加密存儲方案。

如上圖所示,Dropbox首先對用戶密碼做了一次sha512哈希將密碼轉化為64個位元組,然後對sha512的結果使用Bcrypt演算法(每個用戶獨立的鹽、強度為10)計算,最後使用AES演算法和全局唯一的密鑰將Bcrypt演算法的計算結果加密並保存。博文中,Dropbox描述了這三層加密的原因:

  • 首先使用sha512,將用戶密碼歸一化為64位元組hash值。因為兩個原因:一個是Bcrypt算對輸入敏感,如果用戶輸入的密碼較長,可能導致Bcrypt計算過慢從而影響響應時間;另一個是有些Bcrypt演算法的實現會將長輸入直接截斷為72位元組,從資訊理論的角度講,這導致用戶信息的熵變小;
  • 然後使用Bcrypt演算法。選擇Bcrypt的原因,是Dropbox的工程師對這個演算法更熟悉調優更有經驗,參數選擇的標準,是Dropbox的線上API伺服器可以在100ms左右的時間可計算出結果。另外,關於Bcrypt和Scrypt哪個演算法更優,密碼學家也沒有定論。同時,Dropbox也在關注密碼hash演算法新秀Argon2,並表示會在合適的時機引入;
  • 最後使用AES加密。因為Bcrypt不是完美的演算法,所以Dropbox使用AES和全局密鑰進一步降低密碼被破解的風險,為了防止密鑰泄露,Dropbox採用了專用的密鑰保存硬體。Dropbox還提到了最後使用AES加密的另一個好處,即密鑰可定時更換,以降低用戶信息/密鑰泄露帶來的風險。


建議採用phpass的加密類,Wordpress就是用的這個。 http://www.openwall.com/phpass/


簡單來說就是加鹽,使每一個用戶哪怕密碼一樣密文值也不一樣。

另外使用公開的加密方式,使用複雜密碼的安全策略。

再安全的演算法也不能保證一位的安全。總是使用123456這樣的密碼是註定被破解的。


只要這個演算法是不可逆的, 就可以. 所以, 我贊同Flily Hsu的說法, 這不是加密, :)


我用的是這個組合(php語言):substr(base64(md5($PW,$salt)),0,-1)

目前還沒發現網上有現成的這個彩虹表,有需要可以用。

你可以在下面這個網站查查有哪些是早就被收入彩虹表了的演算法組合

http://www.cmd5.com/password.aspx#type


推薦閱讀:

如何破譯這段密碼(Li: H Be C Ne)?
求大神破解密碼?
為什麼 Sparrow for iPhone 沒有推送功能?
一般網站會將密碼加密後儲存到資料庫里去,那網站管理員有沒有許可權查看用戶的密碼呢?
MD5 是不是可以這樣破解?

TAG:網路安全 | 密碼 |