雜湊碰撞與生日相同概率

2021-08-27 08:41:54 字數 3408 閱讀 5039

所謂雜湊(hash),就是將不同的輸入對映成獨一無二的、固定長度的值(又稱"雜湊值")。它是最常見的軟體運算之一。

如果不同的輸入得到了同乙個雜湊值,就發生了"雜湊碰撞"(collision)。

舉例來說,很多網路服務會使用雜湊函式,產生乙個 token,標識使用者的身份和許可權。

afgg2pixh0ht6dmxuxqv4na1pu120r0ymaqhuc13i8
上面這個字串就是乙個雜湊值。如果兩個不同的使用者,得到了同樣的 token,就發生了雜湊碰撞。伺服器將把這兩個使用者視為同乙個人,這意味著,使用者 b 可以讀取和更改使用者 a 的資訊,這無疑帶來了很大的安全隱患。

黑客攻擊的一種方法,就是設法製造"雜湊碰撞",然後入侵系統,竊取資訊。

防止雜湊碰撞的最有效方法,就是擴大雜湊值的取值空間。

16個二進位制位的雜湊值,產生碰撞的可能性是 65536 分之一。也就是說,如果有65537個使用者,就一定會產生碰撞。雜湊值的長度擴大到32個二進位制位,碰撞的可能性就會下降到 4,294,967,296 分之一。

更長的雜湊值意味著更大的儲存空間、更多的計算,將影響效能和成本。開發者必須做出抉擇,在安全與成本之間找到平衡。

下面就介紹,如何在滿足安全要求的前提下,找出雜湊值的最短長度。

雜湊碰撞的概率取決於兩個因素(假設雜湊函式是可靠的,每個值的生成概率都相同)。

這個問題在數學上早有原型,叫做"生日問題"(birthday problem):乙個班級需要有多少人,才能保證每個同學的生日都不一樣?

答案很出人意料。如果至少兩個同學生日相同的概率不超過5%,那麼這個班只能有7個人。事實上,乙個23人的班級有50%的概率,至少兩個同學生日相同;50人班級有97%的概率,70人的班級則是99.9%的概率(計算方法見後文)。

這意味著,如果雜湊值的取值空間是365,只要計算23個雜湊值,就有50%的可能產生碰撞。也就是說,雜湊碰撞的可能性,遠比想象的高。實際上,有乙個近似的公式。

上面公式可以算出,50% 的雜湊碰撞概率所需要的計算次數,n 表示雜湊的取值空間。生日問題的 n 就是365,算出來是 23.9。這個公式告訴我們,雜湊碰撞所需耗費的計算次數,跟取值空間的平方根是乙個數量級。

這種利用雜湊空間不足夠大,而製造碰撞的攻擊方法,就被稱為生日攻擊(birthday attack)。

這一節給出生日攻擊的數學推導。

至少兩個人生日相同的概率,可以先算出所有人生日互不相同的概率,再用 1 減去這個概率。

我們把這個問題設想成,每個人排隊依次進入乙個房間。第乙個進入房間的人,與房間裡已有的人(0人),生日都不相同的概率是365/365;第二個進入房間的人,生日獨一無二的概率是364/365;第三個人是363/365,以此類推。

因此,所有人的生日都不相同的概率,就是下面的公式。

上面公式的 n 表示進入房間的人數。可以看出,進入房間的人越多,生日互不相同的概率就越小。

這個公式可以推導成下面的形式。

那麼,至少有兩個人生日相同的概率,就是 1 減去上面的公式。

上面的公式,可以進一步推導成一般性的、便於計算的形式。

根據泰勒公式,指數函式 ex 可以用多項式展開。

如果 x 是乙個極小的值,那麼上面的公式近似等於下面的形式。

現在把生日問題的1/365代入。

因此,生日問題的概率公式,變成下面這樣。

假設 d 為取值空間(生日問題裡是 365),就得到了一般化公式。

上面就是雜湊碰撞概率的公式。

上面的公式寫成函式。

const calculate = (d, n) => 

calculate(365, 23) // 0.5000017521827107

calculate(365, 50) // 0.9651312540863107

calculate(365, 70) // 0.9986618113807388

一般來說,雜湊值由大小寫字母和阿拉伯數字構成,一共62個字元(10 + 26 + 26)。如果雜湊值只有三個字元的長度(比如abc),取值空間就是62 ^ 3 = 238,328,那麼10000次計算導致的雜湊碰撞概率是100%。

calculate(62 ** 3, 10000) // 1
雜湊值的長度增加到5個字元(比如abcde),碰撞的概率就下降到5.3%。

calculate(62 ** 5, 10000) // 0.05310946204730993
現在有一家公司,它的 api 每秒會收到100萬個請求,每個請求都會生成乙個雜湊值,假定這個 api 會使用10年。那麼,大約一共會計算300萬億次雜湊。能夠接受的雜湊碰撞概率是1000億分之一(即每天發生一次雜湊碰撞),請問雜湊字串最少需要多少個字元?

根據上面的公式倒推,就會知道雜湊值的最短長度是22個字元(比如bwq1w6soxka1pu120r0yma),計算過程略。

22個字元的雜湊值,就能保證300萬億次計算裡面,只有1000億分之一的概率發生碰撞。常用的 sha256 雜湊函式產生的是64個字元的雜湊值,每個字元的取值範圍是0~9和a~f,發生碰撞的概率還要低得多。

(完)

從生日悖論談雜湊碰撞

前幾天和乙個大佬交流了幾個問題,其中乙個關於id生成的問題推展到了雜湊衝突和乙個與之相關的乙個數學趣題生日悖論。雜湊的對映壓縮和衝突 生日悖論 crc32的衝突分析 雜湊的本質就是數學,簡單來說雜湊函式實現了各種長度和形式的輸入經過公開的雜湊函式的運算生成乙個固定長度的串,並且這個過程是單向不可逆的...

JavaScript解決雜湊碰撞

function hash return total this.hash.length this.put function key,value this.show function this.hash i 不同鍵值可能得到相同的hash值,如下 let myhash new hash myhash....

unordered map 碰撞處理 重雜湊

這篇也講的很好 關於負載因子的解釋 c 的hash表中有乙個負載因子loadfactor,當loadfactor 1時,hash表查詢的期望複雜度為o 1 因此,每次往hash表中新增元素時,我們必須保證是在loadfactor 1的情況下,才能夠新增 因此,當hash表中loadfactor超過了...