我如何證明不是為密碼學設計的 PRNG 不適合生成密碼?
這是Stack Overflow 上這個問題的複現。在
class Random
.NET 執行時中,它被設計為用作模擬偽隨機數的廉價快速源 - 非常快,種子可以顯式傳遞,發出易於重現的序列。此類經常被誤用於生成密碼和類似的秘密內容。這是一個問題,顯示了對此 PRNG 的一種可能攻擊,但它僅適用於非常特定的場景(當大量數據連續發出時)並且還需要一些時間 - 在單個 CPU 上長達一年。
是否有一種更清晰、更快捷的方法來表明使用 Random() 類中的數據生成的密碼比通常預期的要弱得多?
在這種情況下,安全 PRG 應該有兩個關鍵的東西,而 Random 可能不會。安全的 PRG 不應允許有權訪問某些輸出的人了解有關任何其他輸出的更多資訊,並且應該安全地播種。
關於第一點:如果您生成的密碼應該是獨立安全的,您必須假設其他密碼將在某個時候為惡意對手所知。(也許攻擊者實際上是您的程序的使用者,因此直接給出了這些密碼。)如果攻擊者以某種方式學習了大量密碼,則會引起人們擔心他可能會(全部或部分)獲得他不應該知道的其他密碼。我對 Random 的 PRG 不太了解,但這就是為什麼 Ilmari 提到的攻擊如此重要的原因。
第二點:種子空間幾乎可以保證是不安全的。Random的預設建構子說:
“預設種子值來源於系統時鐘
$$ … $$通過呼叫預設建構子連續創建的不同 Random 對象將具有相同的預設種子值”。
這意味著種子是完全基於時間戳確定的。
但是:任何僅由時間戳播種的 PRG 都是不安全的。有一種傾向認為攻擊者很難準確猜測時間戳的生成時間,但事實並非如此。如果攻擊者對創建時間戳種子的時間範圍有任何想法,它會限制他們需要攻擊的種子範圍(因為他們只需要從生成種子的時間間隔測試時間戳)。但是個人電腦上的現代時間戳沒有足夠小的解析度來提供足夠大的總空間來抵抗暴力攻擊。
例如,假設您可以獲得精確到一微秒(千分之一毫秒)的時間戳。這比任何現代 PC(據我所知)提供的都要好。這提供了每秒 1,000,000 個可能的時間戳,每天產生 86,400,000,000 個時間戳,這大約是總空間的 37 位。如果攻擊者知道生成種子的時間範圍是 24 小時,這在密碼學上是不安全的並且很容易被暴力破解。在整整一年的時間裡,時間戳空間仍然只有大約 45 位,這在密碼學上也不安全。如果攻擊者只知道為 PRG 生成種子的年份,他們仍然可以暴力破解它。
這些數字更加黯淡,更現實的時間戳解析度僅為每秒 1,000 個時間戳。(Ilmari 的回答表明 C# 可能只精確到毫秒。)這會產生每年只有大約 35 位的時間戳空間。(作為參考,我認為我的舊筆記型電腦可以在不到兩週的時間內攻擊它。)
更糟的是:將使用者參數作為種子的 Random 的建構子只需要一個 32 位整數。這可以防止您生成自己的優質種子並使用它,因為您只能提供 32 位種子。這也意味著對於預設建構子情況,內部種子限制為 32 位。32 位不是加密安全的,並且允許攻擊者暴力破解種子,無論他是否知道種子的原始時間。
相比之下,C# 提供的安全 PRG 應該能夠抵抗使用已知輸出的攻擊,並且應該為種子查詢一個好的熵源(它可能是與 CryptGenRandom 相同的源的包裝器)。
因此,忽略在 Random 類中實現的實際 PRG 的加密強度,我會說它是不安全的,僅基於它的播種。預設種子太可預測並且不夠大。
您在SO 文章中報告的每天6,000,000 次猜測的速度似乎非常低——每秒只有 70 次!
您是否正在嘗試為每個種子生成一整套 10,000 個字元串?這不應該是必要的;繼續生成字元串,直到找到不在給定集合中的字元串,如果種子錯誤,這應該只發生在幾次迭代中。(如果您只有原始字元串的一個子集,您可以進行其他優化,但這會變得更加複雜。)
您可能也不需要測試所有可能的種子;此頁面似乎暗示 .NET 中的預設隨機種子是
(int) DateTime.Now.Ticks & 0x0000FFFF
. 我在 ideone.com 上進行了快速測試,似乎至少有一個DateTime.Now.Ticks
值(定義為十分之一微秒)以 10 的步長遞增。因此,如果您知道種子生成的時間在7分鐘左右,你只需要測試每10個種子;即使您不這樣做,您仍然至少可以跳過所有奇數種子值。綜上所述,直接攻擊生成器可能比專注於播種更有效。從我從文件中可以看出,.NET
Random
類似乎正在使用減法滯後 Fibonacci RNG;我相信有一些已知的方法可以從相對較小的輸出樣本中推斷此類生成器的狀態,儘管我自己對它們並不特別熟悉。