今天我在看極客時間的網課時重新認識了「雜湊演算法」這個概念,之前一直與前一節的雜湊函式搞混了。其實雜湊演算法的作用非常廣泛,雜湊函式只是其中的乙個應用:
首先我們來重新認識一下「雜湊演算法」到底是什麼?它的定義和原理很簡單,基本上一句話就可以概括。將任意長度的二進位制值串對映為固定長度的二進位制值串,這個對映的規則就是雜湊演算法。而通過原始資料對映之後得到的二進位制串就是雜湊值。仔細想一想之前用到過的雜湊函式不就是這個原理麼?我們通過將某個值作為key,雜湊函式返回乙個雜湊值。一般來說返回的這個雜湊值是陣列下標,然後用這個下標我們去到雜湊表進行進一步查詢。相同雜湊值的,但是key不同的,我們可以繼續再對比key值。
而且設計乙個優秀的雜湊演算法也不容易:
1.從雜湊值不能反向推導原始資料(所以雜湊演算法也叫單向雜湊演算法)
2.對輸入資料比較敏感。哪怕原始資料只修改了乙個bit,最後得到的雜湊值也大不相同。
3.雜湊衝突的概率要很小,對於不同的原始資料,雜湊值相同的概率非常小。
4.雜湊演算法的執行效率要盡量高效,針對較長的文字,也能快速地計算雜湊值。
就算是兩個很相似的文字進行雜湊之後,得到的雜湊值也是完全不同的。
安全加密
說到雜湊演算法的應用,最先想到的應該就是安全加密。最常用於加密的雜湊演算法是md5(md5 message-digest algorithm,md5 訊息摘要演算法)和sha(secure hash algorithm,安全雜湊演算法)。除了這兩個之外,當然還有很多其他加密演算法,比如des(data encryption standard,資料加密標準)、aes(advanced encryption standard,高階加密標準)。前面我講到的雜湊演算法四點要求,對用於加密的雜湊演算法來說,有兩點格外重要。第一點是很難根據雜湊值反向推導出原始資料,第二點是雜湊衝突的概率要很小。第一點很好理解,加密的目的就是防止原始資料洩露,所以很難通過雜湊值反向推導原始資料,這是乙個最基本的要求。所以我著重講一下第二點。實際上,不管是什麼雜湊演算法,我們只能儘量減少碰撞衝突的概率,理論上是沒辦法做到完全不衝突的。為什麼這麼說呢?這裡就基於組合數學中乙個非常基礎的理論,鴿巢原理(也叫抽屜原理)。這個原理本身很簡單,它是說,如果有 10 個鴿巢,有 11 只鴿子,那肯定有 1 個鴿巢中的鴿子數量多於 1 個,換句話說就是,肯定有 2 只鴿子在 1 個鴿巢內。有了鴿巢原理的鋪墊之後,我們再來看,為什麼雜湊演算法無法做到零衝突?我們知道,雜湊演算法產生的雜湊值的長度是固定且有限的。比如前面舉的 md5 的例子,雜湊值是固定的 128 位二進位制串,能表示的資料是有限的,最多能表示 2^128 個資料,而我們要雜湊的資料是無窮的。基於鴿巢原理,如果我們對 2^128+1 個資料求雜湊值,就必然會存在雜湊值相同的情況。這裡你應該能想到,一般情況下,雜湊值越長的雜湊演算法,雜湊衝突的概率越低2^128=340282366920938463463374607431768211456
為了讓你能有個更加直觀的感受,我找了兩段字串放在這裡。這兩段字串經過 md5 雜湊演算法加密之後,產生的雜湊值是相同的。不過,即便雜湊演算法存在雜湊衝突的情況,但是因為雜湊值的範圍很大,衝突的概率極低,所以相對來說還是很難破解的。像 md5,有 2^128 個不同的雜湊值,這個資料已經是乙個天文數字了,所以雜湊衝突的概率要小於 1/2^128。如果我們拿到乙個 md5 雜湊值,希望通過毫無規律的窮舉的方法,找到跟這個 md5 值相同的另乙個資料,那耗費的時間應該是個天文數字。所以,即便雜湊演算法存在衝突,但是在有限的時間和資源下,雜湊演算法還是被很難破解的。除此之外,沒有絕對安全的加密。越複雜、越難破解的加密演算法,需要的計算時間也越長。比如 sha-256 比 sha-1 要更複雜、更安全,相應的計算時間就會比較長。密碼學界也一直致力於找到一種快速並且很難被破解的雜湊演算法。我們在實際的開發過程中,也需要權衡破解難度和計算時間,來決定究竟使用哪種加密演算法。
唯一標識
我先來舉乙個例子。如果要在海量的相簿中,搜尋一張圖是否存在,我們不能單純地用的元資訊(比如名稱)來比對,因為有可能存在名稱相同但內容不同,或者名稱不同內容相同的情況。那我們該如何搜尋呢?
我們知道,任何檔案在計算中都可以表示成二進位製碼串,所以,比較笨的辦法就是,拿要查詢的的二進位製碼串與相簿中所有的二進位製碼串一一比對。如果相同,則說明在相簿中存在。但是,每個小則幾十 kb、大則幾 mb,轉化成二進位制是乙個非常長的串,比對起來非常耗時。有沒有比較快的方法呢?
我們可以給每乙個取乙個唯一標識,或者說資訊摘要。比如,我們可以從的二進位製碼串開頭取 100 個位元組,從中間取 100 個位元組,從最後再取 100 個位元組,然後將這 300 個位元組放到一塊,通過雜湊演算法(比如 md5),得到乙個雜湊字串,用它作為的唯一標識。通過這個唯一標識來判定是否在相簿中,這樣就可以減少很多任務作量。
如果還想繼續提高效率,我們可以把每個的唯一標識,和相應的檔案在相簿中的路徑資訊,都儲存在雜湊表中。當要檢視某個是不是在相簿中的時候,我們先通過雜湊演算法對這個取唯一標識,然後在雜湊表中查詢是否存在這個唯一標識。
如果不存在,那就說明這個不在相簿中;如果存在,我們再通過雜湊表中儲存的檔案路徑,獲取到這個已經存在的,跟現在要插入的做全量的比對,看是否完全一樣。如果一樣,就說明已經存在;如果不一樣,說明兩張儘管唯一標識相同,但是並不是相同的。
應用三:資料校驗具體的 bt 協議很複雜,校驗方法也有很多,我來說其中的一種思路。
應用四:雜湊函式
前面講了很多雜湊演算法的應用,實際上,雜湊函式也是雜湊演算法的一種應用。
我們前兩節講到,雜湊函式是設計乙個雜湊表的關鍵。它直接決定了雜湊衝突的概率和雜湊表的效能。不過,相對雜湊演算法的其他應用,雜湊函式對於雜湊演算法衝突的要求要低很多。即便出現個別雜湊衝突,只要不是過於嚴重,我們都可以通過開放定址法或者鍊錶法解決。
不僅如此,雜湊函式對於雜湊演算法計算得到的值,是否能反向解密也並不關心。雜湊函式中用到的雜湊演算法,更加關注雜湊後的值是否能平均分布,也就是,一組資料是否能均勻地雜湊在各個槽中。除此之外,雜湊函式執行的快慢,也會影響雜湊表的效能,所以,雜湊函式用的雜湊演算法一般都比較簡單,比較追求效率。
我們可以通過雜湊演算法,對使用者密碼進行加密之後再儲存,不過最好選擇相對安全的加密演算法,比如 sha 等(因為 md5 已經號稱被破解了)。不過僅僅這樣加密之後儲存就萬事大吉了嗎?
字典攻擊你聽說過嗎?如果使用者資訊被「脫庫」,黑客雖然拿到是加密之後的密文,但可以通過「猜」的方式來破解密碼,這是因為,有些使用者的密碼太簡單。比如很多人習慣用 00000、123456 這樣的簡單數字組合做密碼,很容易就被猜中。
那我們就需要維護乙個常用密碼的字典表,把字典中的每個密碼用雜湊演算法計算雜湊值,然後拿雜湊值跟脫庫後的密文比對。如果相同,基本上就可以認為,這個加密之後的密碼對應的明文就是字典中的這個密碼。(注意,這裡說是的是「基本上可以認為」,因為根據我們前面的學習,雜湊演算法存在雜湊衝突,也有可能出現,儘管密文一樣,但是明文並不一樣的情況。)
針對字典攻擊,我們可以引入乙個鹽(salt),跟使用者的密碼組合在一起,增加密碼的複雜度。我們拿組合之後的字串來做雜湊演算法加密,將它儲存到資料庫中,進一步增加破解的難度。不過我這裡想多說一句,我認為安全和攻擊是一種博弈關係,不存在絕對的安全。所有的安全措施,只是增加攻擊的成本而已。
演算法導論 第十一章 雜湊表
當將乙個域u中的元素對映到乙個雜湊表t中時,我們如何對映?若對映到同一位置怎麼辦?前乙個問題我們通過hash函式來解決,後乙個問題我們通過 衝突處理 解決。1 除法 h k k mod m m的選取 素數,且不要太靠近 2的冪次方 2 乘法 其中,0 eg 當m 8 2 3,w 7時 3 全域雜湊 ...
演算法導論 第十一章 雜湊表
演算法導論 第十章 雜湊表 雜湊表大概就是把給每個要儲存的資料都用雜湊函式給定乙個關鍵字,對應到乙個槽。由於不同的資訊可能對應同樣的關鍵字,也就是說對應同乙個槽,這時要解決其衝突,解決衝突可以用鏈結法,和開放定址法。對於鏈結法,其對於衝突採取鍊錶進行解決,這樣就保證了資料不會發生衝突。其查詢的平均時...
演算法導論 第十一章 雜湊表
雜湊表是實現字典操作的一種有效資料結構。雜湊表是普通陣列概念的推廣,由於對普通陣列可以直接定址,使得能在o 1 時間內訪問陣列中的任意位置。如果儲存空間允許,我們可以提供乙個陣列,為每個可能的關鍵字保留乙個位置,以利用直接定址技術的優勢。當實際儲存的關鍵字數目比全部的可能關鍵字總數要小時,採用雜湊表...